Android新手上路——自定义SurfaceView,重力感应小球

来源:互联网 发布:手机客户端制作软件 编辑:程序博客网 时间:2024/05/07 19:20

如有错误,还请指正
- 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很简单就不贴了。

0 0
原创粉丝点击