如有错误,还请指正
- SurfaceView的使用
- 加速度传感器的使用
- 重力小球View的代码
package com.example.doge_gravityjumpingaty.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
/**
* @author fhbianling--- A little learning is a dangerous thing.
* @mail fhbianling@163.com
* @time 2016-6-26下午4:04:32
*/
public class GravityJumpingView extends SurfaceView implements Runnable, Callback {
/**
* SurfaceView可以在非UI线程中绘制界面,因此在重绘频繁的情况下,可以考虑使用SurfaceView
* 它的回调中两个主要的方法:
* 通过getHolder().lockCanvas()获得一个Canvas对象,在这个Canvas上作画,
* 然后getHolder().unlockCanvasAndPost(Canvas canvas)提交画布,并显示。 注意这三步操作要加同步锁
**/
private Paint mPaint;
private Sensor sensorG;
private SensorManager manager;
private int viewWidth;
private int viewHeight;
private int radiusBall;
private boolean isRunning;
private int centerXmin, centerXmax, centerYmin, centerYmax;
private int centerX, centerY;
private float aX, aY;
private float vX, vY;
private Canvas mCanvas;
private SurfaceHolder mHolder;
/**
* 传感器的监听器
*/
private SensorEventListener mListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
/** 将当前加速度赋值给全局变量用以在线程中更新圆球圆心位置 **/
/** 对于加速度传感器,其event.values[]的0,1,2下标的三个值分别对应当前手机x,y,z方向的加速度 **/
/** 这个坐标系,以手机屏幕左至右为x方向正方向,下至上为y方向正方向,垂直屏幕指向屏幕上方为z正方向 **/
/** 和2d时的相关x,y方向(如onTouchEvent()中的event.getY())不同 **/
aX = event.values[0];
aY = event.values[1];
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
/** 这个回调方法在传感器精度改变时被触发,在这个demo中并不需要做任何操作 **/
}
};
/**
* 初始化传感器和画笔
*
* @param context
*/
private void init(Context context) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
// 通过context获得系统服务-->传感器管理器
manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
// 通过传感器管理器获得加速度传感器,获取其他传感器的方法只需传入不同的Sensor.Type...常量值即可
// 不同的手机拥有的传感器不一定一样,不常用的传感器,一般的手机生产厂商不会提供
// 通过SensorManager对象的
// .getSensorList(Sensor.TYPE_ALL)方法可以返回一个手机拥有的传感器的List<Sensor>集合
sensorG = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// 为SurfaceView的SurfaceHolder添加回调接口,这个接口的三个方法分别在Surface创建,销毁和改变时触发
// 一个surface可以理解为对应的SurfaceView的一个可见区域,并且它是直接对应到内存中的,因此它有自己的生命周期
// 分别就对应了surfaceCreate,surfaceChange,surfaceDestroy三个方法
mHolder = this.getHolder();
mHolder.addCallback(this);
}
public GravityJumpingView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public GravityJumpingView(Context context) {
super(context);
init(context);
}
public GravityJumpingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
/**
* 通过使这个View本身实现Runnable接口来绘制界面, 并通过Runnable的线程休眠达到控制每秒帧数的效果,
* 因为传感器的数据变化较快,控制每秒重绘次数避免绘制太多次。
*/
@Override
public void run() {
// 注册监听器的第三个参数用于控制传感器获取数值的频率
manager.registerListener(mListener, sensorG, SensorManager.SENSOR_DELAY_NORMAL);
Log.d("Listener", "register");
while (isRunning) {
vX -= aX;
// 这里的反向是由于坐标系转换
vY += aY;
centerX += vX;
centerY += vY;
// V1=V0+a*t;sX1=sX0+vX*t;物理公式的简单运用
// 以下两个if...else...结构用于进行边界检测
if (centerX <= centerXmin) {
centerX = centerXmin;
// 使反向后的vX的数值变为之前的4/5是为了模拟碰撞过程中弹性形变造成的动能损失,vY同
vX = -vX * 4 / 5;
} else if (centerX >= centerXmax) {
centerX = centerXmax;
vX = -vX * 4 / 5;
}
if (centerY <= centerYmin) {
centerY = centerYmin;
vY = -vY * 4 / 5;
} else if (centerY >= centerYmax) {
centerY = centerYmax;
vY = -vY * 4 / 5;
}
try {
// 在数据刷新后的绘画过程
synchronized (mHolder) {
// mCanvas=mHolder.lockCanvas(dirty)方法,dirty是一个Rect类的实例,
// 通过这个方法可以只在dirty这个矩形的区域内更新画面,以进一步优化绘制的内存消耗,
//在这里使用的.lockCanvas()方法会绘制整个Canvas区域
mCanvas = mHolder.lockCanvas();
draw();
mHolder.unlockCanvasAndPost(mCanvas);
}
// 使线程睡眠50ms达到近似每秒20帧的帧数
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
// 在循环结束后注销传感器
if (manager != null && mListener != null) {
manager.unregisterListener(mListener);
Log.d("Listener", "unregister");
}
}
/**
* 绘制数据更新后的小球
*/
private void draw() {
if (mPaint == null || mCanvas == null) {
return;
}
// 画白色背景
mPaint.setStyle(Style.FILL_AND_STROKE);
mPaint.setColor(Color.WHITE);
mCanvas.drawRect(0, 0, viewWidth, viewHeight, mPaint);
// 画移动范围边框
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Style.STROKE);
mCanvas.drawRect(0, 0, viewWidth, viewHeight, mPaint);
// 画小球
mPaint.setStyle(Style.FILL_AND_STROKE);
mCanvas.drawCircle(centerX, centerY, radiusBall, mPaint);
}
/**
* 在surface创建时注册监听,初始化数据,并启动绘制线程, 这个方法在onAttachedToWindow()后被调用
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
start();
Log.d("surfaceCreated", "1");
}
/**
* start方法用于初始化数据,并启动绘制线程
*/
private void start() {
isRunning = true;
viewWidth = getWidth();
viewHeight = getHeight();
radiusBall = Math.min(viewWidth, viewHeight) / 20;
centerXmin = radiusBall;
centerXmax = viewWidth - radiusBall;
centerYmin = radiusBall;
centerYmax = viewHeight - radiusBall;
centerX = viewWidth / 2;
centerY = viewHeight / 2;
// centerX,centerY的Min,Max分别对应圆心的最小范围和最大范围
// centerX,centerY代表绘制圆形的实时圆心位置
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d("surfaceChanged", "2");
}
/**
* 在surfaceDestroyed()方法中停止线程,注销传感器注册在run()中执行,
* 这个方法会在onDetachedToWindow()后被调用
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
Log.d("surfaceDestroyed", "3");
}
}
布局参数和Acitivity很简单就不贴了。