Android-17的案例学习一:AccelerometerPlayActivity

来源:互联网 发布:mac部分变成英文 编辑:程序博客网 时间:2024/05/15 23:47

     Android SDK 自带的案例源码都很不错,想学习一下。本着天朝的大无畏分享精神,这里就把自己学习的东西跟大家分享下,共同进步。

    AccelerometerPlay是Android自带例子中的加速度传感器的使用,同时也使用了自定义view。例子的运行效果由于网络不行,图片上传不了,感兴趣的朋友可以网上下载一个源码运行下看看,本博客也会附上源码的链接。

    Android中自带的该例子把所有的类都写在了一起,本人觉得看的麻烦,容易让人思路不清(个人看法),就把他们都分开了。

    主界面AccelerometerPlay类源码如下:

   

* This is an example of using the accelerometer to integrate the device's * acceleration to a position using the Verlet method. This is illustrated with * 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 *//** * 参考资料 weakLock机制浅析:http://blog.sina.com.cn/s/blog_4ad7c2540101n2k2.html * SensorManager:http://www.cnblogs.com/androidez/archive/2013/02/06/2901295. * html * * 传感器的坐标系:http://www.cnblogs.com/mengdd/archive/2013/03/12/2954947.html * @author LIUBO * */public class AccelerometerPlayActivity extends Activity {private SimulationView mSimulationView;/** 电源管理者 */private PowerManager mPowerManager;/** 唤醒锁 */private WakeLock mWakeLock;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 获取一个电池管理者的实例mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);// SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,允许关闭键盘灯// 保持屏幕的高亮mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass().getName());
                //自定义的view,该示例的核心mSimulationView = new SimulationView(this);setContentView(mSimulationView);}@Overrideprotected void onResume() {super.onResume();mWakeLock.acquire();//注册加速度传感器mSimulationView.startSimulation();}@Overrideprotected void onPause() {super.onPause();//解除加速度传感器的注册mSimulationView.stopSimulation();mWakeLock.release();}}
上面的代码主要是传感器的注册和保持屏幕高亮显示。


该示例的核心VIew SImulationView继承View实现传感器的事件监听接口

<pre name="code" class="java">public class SimulationView extends View implements SensorEventListener {// diameter of the balls in meters(以米为单位:小球的直径)public static final float sBallDiameter = 0.004f;/** 传感器管理者 */private SensorManager mSensorManager;/** 窗口管理者 */private WindowManager mWindowManager;private Display mDisplay;private Sensor mAccelerometer;// 重力传感器private float mXDpi;// x轴每英寸有多少像素private float mYDpi;// y轴每英寸有多少像素private float mMetersToPixelsX;// x轴每米有多少像素private float mMetersToPixelsY;// y轴每米有多少像素private Bitmap mBitmap;// 小球的图片private Bitmap mWood;// 背景图片private float mXOrigin;private float mYOrigin;private float mSensorX;// x轴的加速度private float mSensorY;// y轴的加速度private long mSensorTimeStamp;private long mCpuTimeStamp;private float mHorizontalBound;private float mVerticalBound;private final ParticleSystem mParticleSystem = new ParticleSystem();public SimulationView(Context context) {super(context);init(context);}private void init(Context context) {mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);// 获取屏幕的尺寸mDisplay = mWindowManager.getDefaultDisplay();// 获取重力传感器mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// 获取屏幕尺寸参数DisplayMetrics metrics = new DisplayMetrics();mDisplay.getMetrics(metrics);mXDpi = metrics.xdpi;mYDpi = metrics.ydpi;// 1.0英寸等于0.0254米mMetersToPixelsX = mXDpi / 0.0254f;mMetersToPixelsY = mYDpi / 0.0254f;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);Options opts = new Options();opts.inDither = true;opts.inPreferredConfig = Bitmap.Config.RGB_565;mWood = BitmapFactory.decodeResource(getResources(), R.drawable.wood, opts);}public void startSimulation() {// 传感器的注册mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);}public void stopSimulation() {// 传感器解除注册mSensorManager.unregisterListener(this);}/** * 布局改变时更改坐标系的中心和最大活动范围 */@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {// 设置原点的坐标(小球粒子活动的最大范围的中心)mXOrigin = (w - mBitmap.getWidth()) * 0.5f;mYOrigin = (h - mBitmap.getHeight()) * 0.5f;// 小球粒子活动的距离原点的最大x轴和y轴的最大距离mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f);mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f);}@Overridepublic void onSensorChanged(SensorEvent event) {// 传感器的类型不是加速度传感器的话就抛掉if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)return;/** * 这里的坐标点(mSensorX,mSensorY)是在以水平向右为X轴的正方向, 垂直向上为Y轴的正方向的自定义的坐标系中 * 重力传感器的坐标系是始终不变的(不要被自定义的坐标系迷惑,这里是两套坐标系) * 屏幕的旋转会改变自定义坐标系所以坐标点(mSensorX,mSensorY)在屏幕旋转时值会发生变化 (这里的屏幕旋转和手机旋转要区分开) */switch (mDisplay.getRotation()) {// 判断屏幕的旋转角度case Surface.ROTATION_0:// 正常的x-y坐标mSensorX = event.values[0];mSensorY = event.values[1];break;case Surface.ROTATION_90:// 旋转90度mSensorX = -event.values[1];mSensorY = event.values[0];break;case Surface.ROTATION_180:// 旋转180度mSensorX = -event.values[0];mSensorY = -event.values[1];break;case Surface.ROTATION_270:// 旋转270度mSensorX = event.values[1];mSensorY = -event.values[0];break;}// 传感器发生改变的时间(单位:纳秒)mSensorTimeStamp = event.timestamp;// 当前的系统时间(单位:纳秒)mCpuTimeStamp = System.nanoTime();}@Overrideprotected void onDraw(Canvas canvas) {// 画背景canvas.drawBitmap(mWood, 0, 0, null);// 小球粒子运动的总时间final long now = mSensorTimeStamp + (System.nanoTime() - mCpuTimeStamp);/* * 基于重力加速度传感器的数据和当前时间重新计算小球粒子的位置 */mParticleSystem.update(mSensorX, mSensorY, now, mHorizontalBound, mVerticalBound);final Bitmap bitmap = mBitmap;final int count = mParticleSystem.getParticleCount();for (int i = 0; i < count; i++) {/* (改造画布的坐标系统(单位像素)使之适应重力传感器的坐标系统(单位米)) */// 这里的x,y的单位是像素final float x = mXOrigin + mParticleSystem.getPosX(i) * mMetersToPixelsX;final float y = mYOrigin - mParticleSystem.getPosY(i) * mMetersToPixelsY;canvas.drawBitmap(bitmap, x, y, null);}invalidate();}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}}
<span style="background-color: rgb(255, 255, 255);">该View的主要功能:</span>
<span style="background-color: rgb(255, 255, 255);">1、将背景图片和小球资源加载进来,然后画到画布上,传感器的坐标系的单位是米,而自定义的坐标系的单位是像素,所以要对米与像素进行转换。</span>
<span style="background-color: rgb(255, 255, 255);">2、在onSizeChange方法中根据布局的改变重新设定自定义坐标系的原点和x轴和y轴上距离原点的最大值。</span></span>
<span style="background-color: rgb(255, 255, 255);">3、在onSensorChange方法中检测重力传感器的数据变化,并根据屏幕的旋转进行坐标的转换。(因为注册清单中设置了屏幕垂直所以屏幕不会进行旋转)</span></span>
<span style="background-color: rgb(255, 255, 255);">4、更新小球的位置,并在画布上画出小球。</span>
<span style="background-color: rgb(255, 255, 255);">小球的系统控制类ParticleSystem </span>


public class ParticleSystem {// 小球粒子的外接正方形面积private static float sBallDiameter2 = SimulationView.sBallDiameter * SimulationView.sBallDiameter;static final int NUM_PARTICLES = 15;// 小球粒子数量private Particle mBalls[] = new Particle[NUM_PARTICLES];// 小球粒子的数组private long mLastT;// 小球上次更改位置的时间private float mLastDeltaT;// 记录小球相邻两次的位置变化的间隔的时间ParticleSystem() {/* * 初始化所有的小球粒子 */for (int i = 0; i < mBalls.length; i++) {mBalls[i] = new Particle();}}/* * 更新小球粒子的位置 */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 int count = mBalls.length;for (int i = 0; i < count; i++) {Particle ball = mBalls[i];ball.computePhysics(sx, sy, dT, dTC);}}mLastDeltaT = dT;}mLastT = t;}public void update(float sx, float sy, long now, float mHorizontalBound, float mVerticalBound) {updatePositions(sx, sy, now);// 设置最大的迭代次数final int NUM_MAX_ITERATIONS = 10;/* * 解决小球冲突,每个粒子被针对每一个其他测试 粒子碰撞。如果检测到冲突的粒子是 使用无限刚度的假想的弹簧弹走。 */boolean more = true;final int count = mBalls.length;for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) {more = false;for (int i = 0; i < count; i++) {Particle curr = mBalls[i];for (int j = i + 1; j < count; j++) {Particle ball = mBalls[j];float dx = ball.mPosX - curr.mPosX;float dy = ball.mPosY - curr.mPosY;float dd = dx * dx + dy * dy;// 小球的碰撞检测if (dd <= sBallDiameter2) {/* * add a little bit of entropy, after nothing is perfect * in the universe. */dx += ((float) Math.random() - 0.5f) * 0.0001f;dy += ((float) Math.random() - 0.5f) * 0.0001f;dd = dx * dx + dy * dy;// 两个小球粒子的中心之间的距离的二次方// 两个小球之间的距离final float d = (float) Math.sqrt(dd);
//不是很懂final float c = (0.5f * (SimulationView.sBallDiameter - d)) / d;curr.mPosX -= dx * c;curr.mPosY -= dy * c;ball.mPosX += dx * c;ball.mPosY += dy * c;more = true;}}/* * 小球粒子不和墙壁相交 */curr.resolveCollisionWithBounds(mHorizontalBound, mVerticalBound);}}}// 返回粒子的数量public int getParticleCount() {return mBalls.length;}// 返回小球所在的x坐标public float getPosX(int i) {return mBalls[i].mPosX;}// 返回小球所在的y坐标public float getPosY(int i) {return mBalls[i].mPosY;}}
小球粒子的实体对象Particle

/* * 我们的每一个小球粒子保持它的当前和之前的位置,以及加速度。为增加真实性每个粒子都有自己的摩擦 *系数。 */public class Particle {// (小球和桌子以及空气的摩擦参数)private static final float sFriction = 0.1f;//当前位置 float mPosX; float mPosY;//加速度private float mAccelX;private float mAccelY;//上次的位置private float mLastPosX;private float mLastPosY;private float mOneMinusFriction;Particle() {//模拟使每一个粒子小球有不同的摩擦系数final float r = ((float) Math.random() - 0.5f) * 0.2f;mOneMinusFriction = 1.0f - sFriction + r;}public void computePhysics(float sx, float sy, float dT, float dTC) {final float m = 1000.0f; // 我们虚拟物体的质量final float gx = -sx * m;//重力在x轴向的分力final float gy = -sy * m;//重力在y轴向的分力final float invm = 1.0f / m;final float ax = gx * invm;final float ay = gy * invm;final float dTdT = dT * dT;//最新位置的坐标<span style="color:#ff9900;">(红色的代码公式不是很懂)</span>final float x = mPosX + <span style="color:#ff0000;">mOneMinusFriction * dTC * (mPosX - mLastPosX)</span> + mAccelX * dTdT*0.5f;final float y = mPosY + mOneMinusFriction * dTC * (mPosY - mLastPosY) + mAccelY * dTdT*0.5f;mLastPosX = mPosX;mLastPosY = mPosY;mPosX = x;mPosY = y;mAccelX = ax;mAccelY = ay;}/* * 小球粒子不和墙壁相交 */public void resolveCollisionWithBounds(float mHorizontalBound,float mVerticalBound) {final float xmax = mHorizontalBound;final float ymax = mVerticalBound;final float x = mPosX;final float y = mPosY;if (x > xmax) {mPosX = xmax;} else if (x < -xmax) {mPosX = -xmax;}if (y > ymax) {mPosY = ymax;} else if (y < -ymax) {mPosY = -ymax;}}}

小球的控制系统类和实体类代码注释也很详细,就不具体说了,碰撞检测和弹簧的模拟的公式本人看的也不是很懂,这里也就不卖弄了,大家如果谁能很好地解释,希望不吝赐教。


源码下载

0 0