Misc updates to AccelerometerPlayActivity
- Fixes background image scaling
- Reduces number of particles for visual effect
- Misc changes to physics logic
- Use views instead of bitmap for particles
Additonal modifications by trevorjohns:
- Removed unused code
- Simplified duplicate calculations
- Updated minSDK to 11
- Updated targetSdk to 23
- Removed stale taget API annotations
Contributed by: Wojtek KaliciĆski <wkal@google.com>
Change-Id: I104da4100ea0670ed5c06354ea71e37564a77752
diff --git a/sensors/AccelerometerPlay/app/build.gradle b/sensors/AccelerometerPlay/app/build.gradle
index 9b7e671..609f320 100644
--- a/sensors/AccelerometerPlay/app/build.gradle
+++ b/sensors/AccelerometerPlay/app/build.gradle
@@ -6,8 +6,8 @@
defaultConfig {
applicationId "com.example.android.accelerometerplay"
- minSdkVersion 5
- targetSdkVersion 5
+ minSdkVersion 11
+ targetSdkVersion 23
}
buildTypes {
diff --git a/sensors/AccelerometerPlay/app/src/main/java/com/example/android/accelerometerplay/AccelerometerPlayActivity.java b/sensors/AccelerometerPlay/app/src/main/java/com/example/android/accelerometerplay/AccelerometerPlayActivity.java
index f71cf9f..b156852 100644
--- a/sensors/AccelerometerPlay/app/src/main/java/com/example/android/accelerometerplay/AccelerometerPlayActivity.java
+++ b/sensors/AccelerometerPlay/app/src/main/java/com/example/android/accelerometerplay/AccelerometerPlayActivity.java
@@ -16,24 +16,28 @@
package com.example.android.accelerometerplay;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.BitmapFactory.Options;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
+import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Surface;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
+import android.widget.FrameLayout;
/**
* This is an example of using the accelerometer to integrate the device's
@@ -41,7 +45,7 @@
* a very simple particle system comprised of a few iron balls freely moving on
* an inclined wooden table. The inclination of the virtual table is controlled
* by the device's accelerometer.
- *
+ *
* @see SensorManager
* @see SensorEvent
* @see Sensor
@@ -77,6 +81,7 @@
// instantiate our simulation view and set it as the activity's content
mSimulationView = new SimulationView(this);
+ mSimulationView.setBackgroundResource(R.drawable.wood);
setContentView(mSimulationView);
}
@@ -109,90 +114,67 @@
mWakeLock.release();
}
- class SimulationView extends View implements SensorEventListener {
+ class SimulationView extends FrameLayout implements SensorEventListener {
// diameter of the balls in meters
private static final float sBallDiameter = 0.004f;
private static final float sBallDiameter2 = sBallDiameter * sBallDiameter;
- // friction of the virtual table and air
- private static final float sFriction = 0.1f;
+ private final int mDstWidth;
+ private final int mDstHeight;
private Sensor mAccelerometer;
private long mLastT;
- private float mLastDeltaT;
private float mXDpi;
private float mYDpi;
private float mMetersToPixelsX;
private float mMetersToPixelsY;
- private Bitmap mBitmap;
- private Bitmap mWood;
private float mXOrigin;
private float mYOrigin;
private float mSensorX;
private float mSensorY;
- private long mSensorTimeStamp;
- private long mCpuTimeStamp;
private float mHorizontalBound;
private float mVerticalBound;
- private final ParticleSystem mParticleSystem = new ParticleSystem();
-
+ private final ParticleSystem mParticleSystem;
/*
* Each of our particle holds its previous and current position, its
* acceleration. for added realism each particle has its own friction
* coefficient.
*/
- class Particle {
- private float mPosX;
- private float mPosY;
- private float mAccelX;
- private float mAccelY;
- private float mLastPosX;
- private float mLastPosY;
- private float mOneMinusFriction;
+ class Particle extends View {
+ private float mPosX = (float) Math.random();
+ private float mPosY = (float) Math.random();
+ private float mVelX;
+ private float mVelY;
- Particle() {
- // make each particle a bit different by randomizing its
- // coefficient of friction
- final float r = ((float) Math.random() - 0.5f) * 0.2f;
- mOneMinusFriction = 1.0f - sFriction + r;
+ public Particle(Context context) {
+ super(context);
}
- public void computePhysics(float sx, float sy, float dT, float dTC) {
- // Force of gravity applied to our virtual object
- final float m = 1000.0f; // mass of our virtual object
- final float gx = -sx * m;
- final float gy = -sy * m;
+ public Particle(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
- /*
- * F = mA <=> A = F / m We could simplify the code by
- * completely eliminating "m" (the mass) from all the equations,
- * but it would hide the concepts from this sample code.
- */
- final float invm = 1.0f / m;
- final float ax = gx * invm;
- final float ay = gy * invm;
+ public Particle(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
- /*
- * Time-corrected Verlet integration The position Verlet
- * integrator is defined as x(t+dt) = x(t) + x(t) - x(t-dt) +
- * a(t).t^2 However, the above equation doesn't handle variable
- * dt very well, a time-corrected version is needed: x(t+dt) =
- * x(t) + (x(t) - x(t-dt)) * (dt/dt_prev) + a(t).t^2 We also add
- * a simple friction term (f) to the equation: x(t+dt) = x(t) +
- * (1-f) * (x(t) - x(t-dt)) * (dt/dt_prev) + a(t)t^2
- */
- final float dTdT = dT * dT;
- final float x = mPosX + mOneMinusFriction * dTC * (mPosX - mLastPosX) + mAccelX
- * dTdT;
- final float y = mPosY + mOneMinusFriction * dTC * (mPosY - mLastPosY) + mAccelY
- * dTdT;
- mLastPosX = mPosX;
- mLastPosY = mPosY;
- mPosX = x;
- mPosY = y;
- mAccelX = ax;
- mAccelY = ay;
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public Particle(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public void computePhysics(float sx, float sy, float dT) {
+
+ final float ax = -sx/5;
+ final float ay = -sy/5;
+
+ mPosX += mVelX * dT + ax * dT * dT / 2;
+ mPosY += mVelY * dT + ay * dT * dT / 2;
+
+ mVelX += ax * dT;
+ mVelY += ay * dT;
}
/*
@@ -208,13 +190,17 @@
final float y = mPosY;
if (x > xmax) {
mPosX = xmax;
+ mVelX = 0;
} else if (x < -xmax) {
mPosX = -xmax;
+ mVelX = 0;
}
if (y > ymax) {
mPosY = ymax;
+ mVelY = 0;
} else if (y < -ymax) {
mPosY = -ymax;
+ mVelY = 0;
}
}
}
@@ -223,7 +209,7 @@
* A particle system is just a collection of particles
*/
class ParticleSystem {
- static final int NUM_PARTICLES = 15;
+ static final int NUM_PARTICLES = 5;
private Particle mBalls[] = new Particle[NUM_PARTICLES];
ParticleSystem() {
@@ -231,7 +217,10 @@
* Initially our particles have no speed or acceleration
*/
for (int i = 0; i < mBalls.length; i++) {
- mBalls[i] = new Particle();
+ mBalls[i] = new Particle(getContext());
+ mBalls[i].setBackgroundResource(R.drawable.ball);
+ mBalls[i].setLayerType(LAYER_TYPE_HARDWARE, null);
+ addView(mBalls[i], new ViewGroup.LayoutParams(mDstWidth, mDstHeight));
}
}
@@ -242,16 +231,12 @@
private void updatePositions(float sx, float sy, long timestamp) {
final long t = timestamp;
if (mLastT != 0) {
- final float dT = (float) (t - mLastT) * (1.0f / 1000000000.0f);
- if (mLastDeltaT != 0) {
- final float dTC = dT / mLastDeltaT;
+ final float dT = (float) (t - mLastT) / 1000.f /** (1.0f / 1000000000.0f)*/;
final int count = mBalls.length;
for (int i = 0; i < count; i++) {
Particle ball = mBalls[i];
- ball.computePhysics(sx, sy, dT, dTC);
+ ball.computePhysics(sx, sy, dT);
}
- }
- mLastDeltaT = dT;
}
mLastT = t;
}
@@ -297,17 +282,15 @@
// simulate the spring
final float d = (float) Math.sqrt(dd);
final float c = (0.5f * (sBallDiameter - d)) / d;
- curr.mPosX -= dx * c;
- curr.mPosY -= dy * c;
- ball.mPosX += dx * c;
- ball.mPosY += dy * c;
+ final float effectX = dx * c;
+ final float effectY = dy * c;
+ curr.mPosX -= effectX;
+ curr.mPosY -= effectY;
+ ball.mPosX += effectX;
+ ball.mPosY += effectY;
more = true;
}
}
- /*
- * Finally make sure the particle doesn't intersects
- * with the walls.
- */
curr.resolveCollisionWithBounds();
}
}
@@ -334,7 +317,7 @@
* of the acceleration. As an added benefit, we use less power and
* CPU resources.
*/
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
+ mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
}
public void stopSimulation() {
@@ -353,23 +336,21 @@
mMetersToPixelsY = mYDpi / 0.0254f;
// rescale the ball so it's about 0.5 cm on screen
- Bitmap ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
- final int dstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f);
- final int dstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f);
- mBitmap = Bitmap.createScaledBitmap(ball, dstWidth, dstHeight, true);
+ mDstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f);
+ mDstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f);
+ mParticleSystem = new ParticleSystem();
Options opts = new Options();
opts.inDither = true;
opts.inPreferredConfig = Bitmap.Config.RGB_565;
- mWood = BitmapFactory.decodeResource(getResources(), R.drawable.wood, opts);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// compute the origin of the screen relative to the origin of
// the bitmap
- mXOrigin = (w - mBitmap.getWidth()) * 0.5f;
- mYOrigin = (h - mBitmap.getHeight()) * 0.5f;
+ mXOrigin = (w - mDstWidth) * 0.5f;
+ mYOrigin = (h - mDstHeight) * 0.5f;
mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f);
mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f);
}
@@ -405,27 +386,16 @@
mSensorY = -event.values[0];
break;
}
-
- mSensorTimeStamp = event.timestamp;
- mCpuTimeStamp = System.nanoTime();
}
@Override
protected void onDraw(Canvas canvas) {
-
/*
- * draw the background
- */
-
- canvas.drawBitmap(mWood, 0, 0, null);
-
- /*
- * compute the new position of our object, based on accelerometer
+ * Compute the new position of our object, based on accelerometer
* data and present time.
*/
-
final ParticleSystem particleSystem = mParticleSystem;
- final long now = mSensorTimeStamp + (System.nanoTime() - mCpuTimeStamp);
+ final long now = System.currentTimeMillis();
final float sx = mSensorX;
final float sy = mSensorY;
@@ -435,7 +405,6 @@
final float yc = mYOrigin;
final float xs = mMetersToPixelsX;
final float ys = mMetersToPixelsY;
- final Bitmap bitmap = mBitmap;
final int count = particleSystem.getParticleCount();
for (int i = 0; i < count; i++) {
/*
@@ -443,10 +412,10 @@
* the sensors coordinate system with the origin in the center
* of the screen and the unit is the meter.
*/
-
final float x = xc + particleSystem.getPosX(i) * xs;
final float y = yc - particleSystem.getPosY(i) * ys;
- canvas.drawBitmap(bitmap, x, y, null);
+ particleSystem.mBalls[i].setTranslationX(x);
+ particleSystem.mBalls[i].setTranslationY(y);
}
// and make sure to redraw asap
diff --git a/sensors/AccelerometerPlay/app/src/main/res/layout/main.xml b/sensors/AccelerometerPlay/app/src/main/res/layout/main.xml
index 8166ca6..c69b222 100644
--- a/sensors/AccelerometerPlay/app/src/main/res/layout/main.xml
+++ b/sensors/AccelerometerPlay/app/src/main/res/layout/main.xml
@@ -14,9 +14,10 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
+ android:background="@drawable/wood"
>
-</LinearLayout>
+</FrameLayout>