重力感應主要是依靠手機的加速度傳感器(accelerometer)來實現 在Android的開發中一共有八種傳感器但是不一定每一款真機都支持這些傳感器。因為很多功能用戶根本不care的所以可能開發商會把某些功能屏蔽掉。還是得根據真機的實際情況來做開發,今天我們主要來討論加速度傳感器的具體實現方式。
傳感器名稱如下:
加速度傳感器(accelerometer)
陀螺儀傳感器(gyroscope)
環境光照傳感器(light)
磁力傳感器(magnetic field)
方向傳感器(orientation)
壓力傳感器(pressure)
距離傳感器(proximity)
溫度傳感器(temperature)
1. SensorMannager傳感器管理對象手機中的所有傳感器都須要通過SensorMannager來訪問,調用getSystemService(SENSOR_SERVICE)方法就可以拿到當前手機的傳感器管理對象。
Java代碼
- SensorManager mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
2. 實現SensorEventListener接口
說道SensorEventListener接口就不得不說SensorListener接口。在Android1.5一下是通過實現SensorListener接口來捕獲 手機傳感器狀態,但是在1.5以上如果實現這個接口系統會提示你這行代碼已經過期。今天我們不討論SensorListener因為它已經是過時的東西了。主要討論一下SensorEventListener接口。我們須要實現SensorEventListener這個接口 onSensorChanged(SensorEvent event)方法來捕獲手機傳感器的狀態,拿到手機 X軸Y軸Z軸三個方向的重力分量,有了這三個方向的數據重力感應的原理我們就已經學會了,簡單吧 哇咔咔~~
Java代碼
- public void onSensorChanged(SensorEvent e) {
- float x = e.values[SensorManager.DATA_X];
- float y = e.values[SensorManager.DATA_Y];
- float z = e.values[SensorManager.DATA_Z];
- }
如圖所示:上例代碼中 float x y z 3個方向的取值范圍是在 -10 到 10 之間,我向同學們說明一下 X軸 Y軸 Z軸 重力分量的含義。 這裡須要注意的是坐標原點 向天空為正數 向地面為負數 剛好與編程時坐標是相反的。
手機屏幕向左側方當X軸就朝向天空,垂直放置 這時候 Y 軸 與 Z軸沒有重力分量,因為X軸朝向天空所以它的重力分量則最大 。這時候X軸 Y軸 Z軸的重力分量的值分別為(10,0,0)
手機屏幕向右側方當X軸就朝向地面,垂直放置 這時候 Y 軸 與 Z軸沒有重力分量,因為X軸朝向地面所以它的重力分量則最小 。這時候X軸 Y軸 Z軸的重力分量的值分別為(-10,0,0)
手機屏幕垂直豎立放置方當Y軸就朝向天空,垂直放置 這時候 X 軸 與 Z軸沒有重力分量,因為Y軸朝向天空所以它的重力分量則最大 。這時候X軸 Y軸 Z軸的重力分量的值分別為(0,10,0)
手機屏幕垂直豎立放置方當Y軸就朝向地面,垂直放置 這時候 X 軸 與 Z軸沒有重力分量,因為Y軸朝向地面所以它的重力分量則最小 。這時候X軸 Y軸 Z軸的重力分量的值分別為(0,-10,0)
手機屏幕向上當Z軸就朝向天空,水平放置 這時候 X 軸與Y軸沒有重力分量,因為Z軸朝向天空所以它的重力分量則最大 。這時候X軸 Y軸 Z軸的重力分量的值分別為(0,0,10)
手機屏幕向上當Z軸就朝向地面,水平放置 這時候 X 軸與Y軸沒有重力分量,因為Z軸朝向地面所以它的重力分量則最小 。這時候X軸 Y軸 Z軸的重力分量的值分別為(0,0,-10)
因為這張圖片是在模擬器上截得,所以沒有重力感應它的三個方向的的重力分量都為0。
3. 注冊SensorEventListener
使用SensorMannager調用getDefaultSensor(Sensor.TYPE_ACCELEROMETER)方法拿到加速重力感應的Sensor對象。因為本章我們討論重力加速度傳感器所以參數為Sensor.TYPE_ACCELEROMETER,如果須要拿到其它的傳感器須要傳入對應的名稱。使用SensorMannager調用registerListener()方法來注冊,第三個參數是檢測的靈敏精確度根據不同的需求來選擇精准度,游戲開發建議使用 SensorManager.SENSOR_DELAY_GAME。
Java代碼
- mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
- mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- // 注冊listener,第三個參數是檢測的精確度
- //SENSOR_DELAY_FASTEST 最靈敏 因為太快了沒必要使用
- //SENSOR_DELAY_GAME 游戲開發中使用
- //SENSOR_DELAY_NORMAL 正常速度
- //SENSOR_DELAY_UI 最慢的速度
- mSensorMgr.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
重力感應簡單速度計算的方式。 每次搖晃手機計算出 X軸 Y軸 Z軸的重力分量可以將它們記錄下來 然後每次搖晃的重力分量和之前的重力分量可以做一個對比 利用差值和時間就可以計算出他們的移動速度。(下面這段代碼是我之前的博文中摘錄過來的,因為那篇寫的不是很好所以在這一篇中我詳細總結一下)
Java代碼
- private SensorManager sensorMgr;
- Sensor sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-
- //保存上一次 x y z 的坐標
- float bx = 0;
- float by = 0;
- float bz = 0;
- long btime = 0;//這一次的時間
- sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
- SensorEventListener lsn = new SensorEventListener() {
- public void onSensorChanged(SensorEvent e) {
- float x = e.values[SensorManager.DATA_X];
- float y = e.values[SensorManager.DATA_Y];
- float z = e.values[SensorManager.DATA_Z];
- //在這裡我們可以計算出 X Y Z的數值 下面我們就可以根據這個數值來計算搖晃的速度了
- //我想大家應該都知道計算速度的公事 速度 = 路程/時間
- //X軸的速度
- float speadX = (x - bx) / (System.currentTimeMillis() - btime);
- //y軸的速度
- float speadY = (y - by) / (System.currentTimeMillis() - btime);
- //z軸的速度
- float speadZ = (z - bz) / (System.currentTimeMillis() - btime);
- //這樣簡單的速度就可以計算出來 如果你想計算加速度 也可以 在運動學裡,加速度a與速度,
- //位移都有關系:Vt=V0+at,S=V0*t+1/2at^2, S=(Vt^2-V0^2)/(2a),根據這些信息也可以求解a。
- //這裡就不詳細介紹了 公事 應該初中物理課老師就教了呵呵~~
-
- bx = x;
- by = y;
- bz = z;
-
- btime = System.currentTimeMillis();
-
- }
-
- public void onAccuracyChanged(Sensor s, int accuracy) {
- }
- };
- // 注冊listener,第三個參數是檢測的精確度
- sensorMgr.registerListener(lsn, sensor, SensorManager.SENSOR_DELAY_GAME);
下面給出這個DEMO小球重力感應的完整代碼
Java代碼
- import android.app.Activity;
- import android.content.Context;
- import android.content.pm.ActivityInfo;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.hardware.Sensor;
- import android.hardware.SensorEvent;
- import android.hardware.SensorEventListener;
- import android.hardware.SensorManager;
- import android.os.Bundle;
- import android.view.SurfaceHolder;
- import android.view.SurfaceView;
- import android.view.Window;
- import android.view.WindowManager;
- import android.view.SurfaceHolder.Callback;
-
- public class SurfaceViewAcitvity extends Activity {
-
- MyView mAnimView = null;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 全屏顯示窗口
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- //強制橫屏
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
-
- // 顯示自定義的游戲View
- mAnimView = new MyView(this);
- setContentView(mAnimView);
- }
-
- public class MyView extends SurfaceView implements Callback,Runnable ,SensorEventListener{
-
- /**每50幀刷新一次屏幕**/
- public static final int TIME_IN_FRAME = 50;
-
- /** 游戲畫筆 **/
- Paint mPaint = null;
- Paint mTextPaint = null;
- SurfaceHolder mSurfaceHolder = null;
-
- /** 控制游戲更新循環 **/
- boolean mRunning = false;
-
- /** 游戲畫布 **/
- Canvas mCanvas = null;
-
- /**控制游戲循環**/
- boolean mIsRunning = false;
-
- /**SensorManager管理器**/
- private SensorManager mSensorMgr = null;
- Sensor mSensor = null;
-
- /**手機屏幕寬高**/
- int mScreenWidth = 0;
- int mScreenHeight = 0;
-
- /**小球資源文件越界區域**/
- private int mScreenBallWidth = 0;
- private int mScreenBallHeight = 0;
-
- /**游戲背景文件**/
- private Bitmap mbitmapBg;
-
- /**小球資源文件**/
- private Bitmap mbitmapBall;
-
- /**小球的坐標位置**/
- private float mPosX = 200;
- private float mPosY = 0;
-
- /**重力感應X軸 Y軸 Z軸的重力值**/
- private float mGX = 0;
- private float mGY = 0;
- private float mGZ = 0;
-
- public MyView(Context context) {
- super(context);
- /** 設置當前View擁有控制焦點 **/
- this.setFocusable(true);
- /** 設置當前View擁有觸摸事件 **/
- this.setFocusableInTouchMode(true);
- /** 拿到SurfaceHolder對象 **/
- mSurfaceHolder = this.getHolder();
- /** 將mSurfaceHolder添加到Callback回調函數中 **/
- mSurfaceHolder.addCallback(this);
- /** 創建畫布 **/
- mCanvas = new Canvas();
- /** 創建曲線畫筆 **/
- mPaint = new Paint();
- mPaint.setColor(Color.WHITE);
- /**加載小球資源**/
- mbitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
- /**加載游戲背景**/
- mbitmapBg = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);
-
- /**得到SensorManager對象**/
- mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
- mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- // 注冊listener,第三個參數是檢測的精確度
- //SENSOR_DELAY_FASTEST 最靈敏 因為太快了沒必要使用
- //SENSOR_DELAY_GAME 游戲開發中使用
- //SENSOR_DELAY_NORMAL 正常速度
- //SENSOR_DELAY_UI 最慢的速度
- mSensorMgr.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
- }
-
- private void Draw() {
-
- /**繪制游戲背景**/
- mCanvas.drawBitmap(mbitmapBg,0,0, mPaint);
- /**繪制小球**/
- mCanvas.drawBitmap(mbitmapBall, mPosX,mPosY, mPaint);
- /**X軸 Y軸 Z軸的重力值**/
- mCanvas.drawText("X軸重力值 :" + mGX, 0, 20, mPaint);
- mCanvas.drawText("Y軸重力值 :" + mGY, 0, 40, mPaint);
- mCanvas.drawText("Z軸重力值 :" + mGZ, 0, 60, mPaint);
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {
-
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- /**開始游戲主循環線程**/
- mIsRunning = true;
- new Thread(this).start();
- /**得到當前屏幕寬高**/
- mScreenWidth = this.getWidth();
- mScreenHeight = this.getHeight();
-
- /**得到小球越界區域**/
- mScreenBallWidth = mScreenWidth - mbitmapBall.getWidth();
- mScreenBallHeight = mScreenHeight - mbitmapBall.getHeight();
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- mIsRunning = false;
- }
-
- @Override
- public void run() {
- while (mIsRunning) {
-
- /** 取得更新游戲之前的時間 **/
- long startTime = System.currentTimeMillis();
-
- /** 在這裡加上線程安全鎖 **/
- synchronized (mSurfaceHolder) {
- /** 拿到當前畫布 然後鎖定 **/
- mCanvas = mSurfaceHolder.lockCanvas();
- Draw();
- /** 繪制結束後解鎖顯示在屏幕上 **/
- mSurfaceHolder.unlockCanvasAndPost(mCanvas);
- }
-
- /** 取得更新游戲結束的時間 **/
- long endTime = System.currentTimeMillis();
-
- /** 計算出游戲一次更新的毫秒數 **/
- int diffTime = (int) (endTime - startTime);
-
- /** 確保每次更新時間為50幀 **/
- while (diffTime <= TIME_IN_FRAME) {
- diffTime = (int) (System.currentTimeMillis() - startTime);
- /** 線程等待 **/
- Thread.yield();
- }
-
- }
-
- }
-
- @Override
- public void onAccuracyChanged(Sensor arg0, int arg1) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- mGX = event.values[SensorManager.DATA_X];
- mGY= event.values[SensorManager.DATA_Y];
- mGZ = event.values[SensorManager.DATA_Z];
-
- //這裡乘以2是為了讓小球移動的更快
- mPosX -= mGX * 2;
- mPosY += mGY * 2;
-
- //檢測小球是否超出邊界
- if (mPosX < 0) {
- mPosX = 0;
- } else if (mPosX > mScreenBallWidth) {
- mPosX = mScreenBallWidth;
- }
- if (mPosY < 0) {
- mPosY = 0;
- } else if (mPosY > mScreenBallHeight) {
- mPosY = mScreenBallHeight;
- }
- }
- }
- }
源碼下載地址:http://vdisk.weibo.com/s/aajXn