方向傳感器會返回三個值,其中第二個角度值表明底部翹起的角度(當頂剖翹起時爲負值);第三個角度值表明右側翹起的角度(當左側翹起時爲負值);根據這兩個角度值就可開發出水平儀了。
假設咱們以大透明圓盅的中心爲原點,當手機頂部翹起時,氣泡應該向頂部移動,也就是氣泡的位置的y座標(2D繪圖座標系,屏幕左上角爲原點)應減少;當手機底部翹起時,氣泡應該向底部移動,也就是氣泡的位置的Y座標應增長——假設氣泡開始位於人透明圓盤的
中心,氣泡的Y座標的改變正好與方向傳感器返同的第二個參數返同的角度的正負相符,所以根據方向傳感器返回的第二個參數來計算氣泡的Y座標便可;與此相似,當手機左側翹起時,氣泡應該同左側移動,也就足氣泡的位置的X座標(2D繪圖座標系,屏幕左上角爲原點)應
減少;當手機右側翹起時,氣泡應該向右側移動,也就是氣泡的位置的X座標應增大——假設氣泡開始位於大透明圓盤的中心,氣泡的X座標的改變正好與方向傳感器返回的第二個參數返同的角度的正負相符,所以根據方向傳感囂返同的第二個參數來計算氣泡的X座標便可。
經過上而介紹的方式來動態改變程序界面中氣泡的位置——手機哪端翹起,水平儀中的氣泡就浮向哪端,這就是水平儀的實現思想。
java
main.xmlandroid
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#fff" android:orientation="vertical" > <org.crazyit.sensor.MyView android:id="@+id/show" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </FrameLayout>
MyView.javacanvas
/** * */ package org.crazyit.sensor; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; /** * Description: <br/> * 網站: <a href="http://www.crazyit.org">瘋狂Java聯盟</a> <br/> * Copyright (C), 2001-2014, Yeeku.H.Lee <br/> * This program is protected by copyright laws. <br/> * Program Name: <br/> * Date: * * @author Yeeku.H.Lee kongyeeku@163.com * @version 1.0 */ public class MyView extends View { // 定義水平儀儀表盤圖片 Bitmap back; // 定義水平儀中的氣泡圖標 Bitmap bubble; // 定義水平儀中氣泡 的X、Y座標 int bubbleX, bubbleY; public MyView(Context context, AttributeSet attrs) { super(context, attrs); // 加載水平儀圖片和睦泡圖片 back = BitmapFactory.decodeResource(getResources(), R.drawable.back); bubble = BitmapFactory .decodeResource(getResources(), R.drawable.bubble); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 繪製水平儀表盤圖片 canvas.drawBitmap(back, 0, 0, null); // 根據氣泡座標繪製氣泡 canvas.drawBitmap(bubble, bubbleX, bubbleY, null); } }
Gradienter.javaapp
package org.crazyit.sensor; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; /** * Description: <br/> * site: <a href="http://www.crazyit.org">crazyit.org</a> <br/> * Copyright (C), 2001-2014, Yeeku.H.Lee <br/> * This program is protected by copyright laws. <br/> * Program Name: <br/> * Date: * * @author Yeeku.H.Lee kongyeeku@163.com * @version 1.0 */ public class Gradienter extends Activity implements SensorEventListener { // 定義水平儀的儀表盤 MyView show; // 定義水平儀能處理的最大傾斜角,超過該角度,氣泡將直接在位於邊界。 int MAX_ANGLE = 30; // 定義Sensor管理器 SensorManager mSensorManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 獲取水平儀的主組件 show = (MyView) findViewById(R.id.show); // 獲取傳感器管理服務 mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); } @Override public void onResume() { super.onResume(); // 爲系統的方向傳感器註冊監聽器 mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME); } @Override protected void onPause() { // 取消註冊 mSensorManager.unregisterListener(this); super.onPause(); } @Override protected void onStop() { // 取消註冊 mSensorManager.unregisterListener(this); super.onStop(); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { float[] values = event.values; // 獲取觸發event的傳感器類型 int sensorType = event.sensor.getType(); switch (sensorType) { case Sensor.TYPE_ORIENTATION: // 獲取與Y軸的夾角 float yAngle = values[1]; // 獲取與Z軸的夾角 float zAngle = values[2]; // 氣泡位於中間時(水平儀徹底水平),氣泡的X、Y座標 int x = (show.back.getWidth() - show.bubble.getWidth()) / 2; int y = (show.back.getHeight() - show.bubble.getHeight()) / 2; // 若是與Z軸的傾斜角還在最大角度以內 if (Math.abs(zAngle) <= MAX_ANGLE) { // 根據與Z軸的傾斜角度計算X座標的變化值(傾斜角度越大,X座標變化越大) int deltaX = (int) ((show.back.getWidth() - show.bubble .getWidth()) / 2 * zAngle / MAX_ANGLE); x += deltaX; } // 若是與Z軸的傾斜角已經大於MAX_ANGLE,氣泡應到最左邊 else if (zAngle > MAX_ANGLE) { x = 0; } // 若是與Z軸的傾斜角已經小於負的MAX_ANGLE,氣泡應到最右邊 else { x = show.back.getWidth() - show.bubble.getWidth(); } // 若是與Y軸的傾斜角還在最大角度以內 if (Math.abs(yAngle) <= MAX_ANGLE) { // 根據與Y軸的傾斜角度計算Y座標的變化值(傾斜角度越大,Y座標變化越大) int deltaY = (int) ((show.back.getHeight() - show.bubble .getHeight()) / 2 * yAngle / MAX_ANGLE); y += deltaY; } // 若是與Y軸的傾斜角已經大於MAX_ANGLE,氣泡應到最下邊 else if (yAngle > MAX_ANGLE) { y = show.back.getHeight() - show.bubble.getHeight(); } // 若是與Y軸的傾斜角已經小於負的MAX_ANGLE,氣泡應到最右邊 else { y = 0; } // 若是計算出來的X、Y座標還位於水平儀的儀表盤內,更新水平儀的氣泡座標 if (isContain(x, y)) { show.bubbleX = x; show.bubbleY = y; } // 通知系統重回MyView組件 show.postInvalidate(); break; } } // 計算x、y點的氣泡是否處於水平儀的儀表盤內 private boolean isContain(int x, int y) { // 計算氣泡的圓心座標X、Y int bubbleCx = x + show.bubble.getWidth() / 2; int bubbleCy = y + show.bubble.getWidth() / 2; // 計算水平儀儀表盤的圓心座標X、Y int backCx = show.back.getWidth() / 2; int backCy = show.back.getWidth() / 2; // 計算氣泡的圓心與水平儀儀表盤的圓心之間的距離。 double distance = Math.sqrt((bubbleCx - backCx) * (bubbleCx - backCx) + (bubbleCy - backCy) * (bubbleCy - backCy)); // 若兩個圓心的距離小於它們的半徑差,便可認爲處於該點的氣泡依然位於儀表盤內 if (distance < (show.back.getWidth() - show.bubble.getWidth()) / 2) { return true; } else { return false; } } }