編輯:關於Android編程
Android的傳感器開發
Android的常用傳感器
傳感器應用案例
Android的傳感器開發
1.1 開發傳感器應用
開發傳感器的步驟如下:
調用Context的getSystemService(Context.SENSOR_SERVICE)方法獲取SensorManager對象。
調用SensorManager的getDefaultSensor(int type)方法來獲取指定類型的傳感器。
一般在Activity的onResume()方法中調用SensorManager的registerListener()為指定傳感器注冊監聽器即可。程序可以通過實現監聽器即可獲取傳感器傳回來的數據。
SersorManager提供的注冊傳感器的方法為registerListener(SensorListener listener, Sensor sensor, int rate)該方法中三個參數說明如下:
listener:監聽傳感器事件的監聽器
sensor:傳感器對象
rate:指定獲取傳感器數據的頻率
rate可以獲取傳感器數據的頻率,支持如下幾個頻率值:
SENSOR_DELAY_FASTEST:最快,延遲最小。
SENSOR_DELAY_GAME:適合游戲的頻率。
SENSOR_DELAY_NORMAL:正常頻率
SENSOR_DELAY_UI:適合普通用戶界面的頻率。
例:加速度傳感器:
AccelerometerTest.java
public class AccelerometerTest extends Activity implements SensorEventListener { // 定義系統的Sensor管理器 SensorManager sensorManager; EditText etTxt1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 獲取程序界面上的文本框組件 etTxt1 = (EditText) findViewById(R.id.txt1); // 獲取系統的傳感器管理服務 sensorManager = (SensorManager) getSystemService( Context.SENSOR_SERVICE); } @Override protected void onResume() { super.onResume(); // 為系統的加速度傳感器注冊監聽器 sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME); } @Override protected void onStop() { // 取消注冊 sensorManager.unregisterListener(this); super.onStop(); } // 以下是實現SensorEventListener接口必須實現的方法 // 當傳感器的值發生改變時回調該方法 @Override public void onSensorChanged(SensorEvent event) { float[] values = event.values; StringBuilder sb = new StringBuilder(); sb.append(X方向上的加速度:); sb.append(values[0]); sb.append( Y方向上的加速度:); sb.append(values[1]); sb.append( Z方向上的加速度:); sb.append(values[2]); etTxt1.setText(sb.toString()); } // 當傳感器精度改變時回調該方法。 @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }
需要指出的是,傳感器的坐標系統與屏幕坐標系統不同,傳感器坐標系統的X軸沿屏幕向右;Y軸則沿屏幕向上,Z軸在垂直屏幕向上。
當拿著手機橫向左右移動時,可能產生X軸上的加速度;拿著手機前後移動時,可能產生Y軸上的加速度;當拿著手機豎向上下移動時,可能產生Z軸上的加速度。
1.2 下載和安裝SensorSimulator
SensorSimulator是傳感器的模擬工具,安裝這個模擬工具之後,開發者就可以在Android模擬器上開發、調試傳感器應用。
SensorSimulator,由PC端程序和手機端程序組成,當兩端的程序運行並建立連接之後,用戶可以通過PC端的程序來改變手機的傳感數據。
下載和安裝SensorSimulator步驟如下:
登錄到http://code.google.com/p/openintents/wiki/SensorSimulator站點或FTP上,下載SensorSimulator的最新版本。
下載SensorSimulator工具後,下載完成後得到一個sensorsimulator-2.0-rc1.zip壓縮包。解壓該文件,得到如下文件結構。
安裝SensorSimulator的手機端程序。通過命令窗口進入到上面文件的bin目錄下,輸入如下命令來安裝SensorSimulatorSettings-2.0-rc1.apk文件。adb install SensorSimulatorSettings-2.0-rc1.apk
運行SensorSimulator的PC端程序,通過命令窗口進入到上面文件的bin目錄下,並在窗口內執行如下命令:java –jar sensorsimulator-2.0-rc1.jar。運行該程序出現如下界面。
運行SensorSimulator的手機端程序。
在SensorSimulator的手機端程序中填寫SensorSimulator的PC端程序的監聽IP地址、監聽端口。
切換到SensorSimulator的Testting Tab頁,單擊該Tab裡的Connect按鈕,SensorSimulator手機端和PC端連接。
1.3 利用SensorSimulator開發傳感器應用
通過使用SensorSimulator,接下來就可以在Android模擬器中開發、調試傳感器應用了。不過使用SensorSimulator開發傳感器應用與開發真實的傳感器應用略有區別。
Android應用必須通過引用外部JAR包的形式來引用SensorSimulator的lib目錄下的sensorsimulator-2.0-rc1.jar包。
在應用項目上右鍵單擊選擇“Build Path” à “Add External Archives…”,找到sensorsimulator-2.0-rc1.jar所在位置,將其添加到項目中。
應用程序編程使用SensorManagerSimulator代替了原有的SensorManager。
應用程序獲取SensorManagerSimulator之後,需要調用connectSimulator()方法連接模擬器。
應用程序編程時所用的Sensor、SensorEvent、 SensorEventListener等不再是Android提供的類,而是由SensorSimulator提供的類。
應用程序需要訪問網絡的權限。
例:利用傳感模擬工具開發加速度傳感器:
AccelSimulatorTest.java
import org.openintents.sensorsimulator.hardware.SensorManagerSimulator; import org.openintents.sensorsimulator.hardware.Sensor; import org.openintents.sensorsimulator.hardware.SensorEvent; import org.openintents.sensorsimulator.hardware.SensorEventListener; import android.app.Activity; import android.hardware.SensorManager; import android.os.Bundle; import android.widget.EditText; public class AccelSimulatorTest extends Activity implements SensorEventListener { // 定義模擬器的Sensor管理器 private SensorManagerSimulator mSensorManager; // 定義界面上的文本框組件 EditText etTxt1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 獲取程序界面的文本框組件 etTxt1 = (EditText) findViewById(R.id.txt1); // 獲取傳感器模擬器的傳感器管理服務 mSensorManager = SensorManagerSimulator.getSystemService( this, SENSOR_SERVICE); // 連接傳感器模擬器 mSensorManager.connectSimulator(); } @Override protected void onResume() { super.onResume(); // 為系統的加速度傳感器注冊監聽器 mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME); } @Override protected void onStop() { // 取消注冊 mSensorManager.unregisterListener(this); super.onStop(); } // 以下是實現SensorEventListener接口必須實現的方法 // 當傳感器的值發生改變時回調該方法 @Override public void onSensorChanged(SensorEvent event) { float[] values = event.values; StringBuilder sb = new StringBuilder(); sb.append(X方向上的加速度:); sb.append(values[0]); sb.append( Y方向上的加速度:); sb.append(values[1]); sb.append( Z方向上的加速度:); sb.append(values[2]); etTxt1.setText(sb.toString()); } @Override // 當傳感器精度改變時回調該方法。 public void onAccuracyChanged(Sensor sensor, int accuracy) { } }
Android的常用傳感器
2.1加速度傳感器Accelerometer
加速度傳感器主要感應手機的運動,在注冊了傳感器監聽器後加速度傳感器主要捕獲3個參數values[0]、values[1]、values[2]。
values[0]:空間坐標系中x軸方向上的加速度減去重力加速度減去中立加速度在x軸上的分量。
values[1]:空間坐標系中x軸方向上的加速度減去重力加速度減去中立加速度在y軸上的分量。
values[2]:空間坐標系中x軸方向上的加速度減去重力加速度減去中立加速度在z軸上的分量。
上述3個數據的單位均為米每二次方秒。
距離說明:
當手機平放到桌面靜止時,加速度為重力加速度g,通過0減去-g(重力加速度g方向為z軸反方向,故為負值)得到values[2]為g。
如果把手機水平方向右推,此時手機x方向上的加速度為正,即values[0]為正。
當把手機以a米每二次方秒的加速度豎值向上舉時,values[2]的返回值為(a+g)米每二次方秒,通過a減去-g得到。
2.2 方向傳感器Orientation
方向傳感器主要感應手機方位的變化,其每次讀取的都是靜態的狀態值,在注冊了傳感器監聽器後方向傳感器主要捕獲3個參數values[0]、values[1]、values[2],關於三個角度的說明如下:
第一個角度:表示手機頂部朝向與正北方向的夾角。當手機繞著Z軸旋轉時,該角度值發生改變。
第二個角度:表示手機頂部或尾部翹起的角度,當手機繞著X軸傾斜時,該角度值發生變化。
第三個角度:表示手機左側或右側翹起的角度。當手機繞著Y軸傾斜時,該角度值發生變化。
2.3磁場傳感器Magnetic Field
磁場傳感器主要用於感應周圍的磁感應強度。即使周圍沒有任何直接的磁場,手機設備也始終會處於地球磁場中。隨著手機狀態設備擺放狀態的改變,周圍磁場在手機的X、Y、Z方向上的會發生改變。
磁場傳感器傳感器會返回三個數據,三個數據分別代表周圍磁場分解到X、Y、Z三個方向上的磁場分量。磁場數據的單位是微特斯拉(uT)。
2.4光傳感器Light
光傳感器用於感應周圍的光強,注冊監聽器後只捕獲一個參數:values[0]。該參數代表周圍的光照強度,單位為勒克斯(lux)。
2.5溫度傳感器Temperature
溫度傳感器用於獲取手機設備所處環境的溫度。溫度傳感器會返回一個數據,該數據代表手機設備周圍的溫度,單位是攝氏度。
2.6壓力傳感器 Pressure
壓力傳感器用於獲取手機設備所處環境的壓力的大小。壓力傳感器會返回一個數據,代表手機設備周圍的壓力大小。
例:傳感器應用:
SensorSimulatorTest.java
public class SensorSimulatorTest extends Activity implements SensorEventListener { // // 定義真機的Sensor管理器 // private SensorManager mSensorManager; // 定義模擬器的Sensor管理器 private SensorManagerSimulator mSensorManager; EditText etOrientation; EditText etMagnetic; EditText etTemerature; EditText etLight; EditText etPressure; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 獲取界面上的EditText組件 etOrientation = (EditText) findViewById(R.id.etOrientation); etMagnetic = (EditText) findViewById(R.id.etMagnetic); etTemerature = (EditText) findViewById(R.id.etTemerature); etLight = (EditText) findViewById(R.id.etLight); etPressure = (EditText) findViewById(R.id.etPressure); // 獲取真機的傳感器管理服務 // mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); // 獲取傳感器模擬器的傳感器管理服務 mSensorManager = SensorManagerSimulator.getSystemService(this, SENSOR_SERVICE); // 連接傳感器模擬器 mSensorManager.connectSimulator(); } @Override protected void onResume() { super.onResume(); // 為系統的方向傳感器注冊監聽器 mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME); // 為系統的磁場傳感器注冊監聽器 mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_GAME); // 為系統的溫度傳感器注冊監聽器 mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE), SensorManager.SENSOR_DELAY_GAME); // 為系統的光傳感器注冊監聽器 mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT), SensorManager.SENSOR_DELAY_GAME); // 為系統的壓力傳感器注冊監聽器 mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE), SensorManager.SENSOR_DELAY_GAME); } @Override protected void onStop() { // 程序退出時取消注冊傳感器監聽器 mSensorManager.unregisterListener(this); super.onStop(); } @Override protected void onPause() { // 程序暫停時取消注冊傳感器監聽器 mSensorManager.unregisterListener(this); super.onPause(); } // 以下是實現SensorEventListener接口必須實現的方法 @Override // 當傳感器精度改變時回調該方法。 public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { float[] values = event.values; // // 真機上獲取觸發event的傳感器類型 // int sensorType = event.sensor.getType(); // 模擬器上獲取觸發event的傳感器類型 int sensorType = event.type; StringBuilder sb = null; // 判斷是哪個傳感器發生改變 switch (sensorType) { // 方向傳感器 case Sensor.TYPE_ORIENTATION: sb = new StringBuilder(); sb.append(繞Z軸轉過的角度:); sb.append(values[0]); sb.append( 繞X軸轉過的角度:); sb.append(values[1]); sb.append( 繞Y軸轉過的角度:); sb.append(values[2]); etOrientation.setText(sb.toString()); break; // 磁場傳感器 case Sensor.TYPE_MAGNETIC_FIELD: sb = new StringBuilder(); sb.append(X方向上的角度:); sb.append(values[0]); sb.append( Y方向上的角度:); sb.append(values[1]); sb.append( Z方向上的角度:); sb.append(values[2]); etMagnetic.setText(sb.toString()); break; // 溫度傳感器 case Sensor.TYPE_TEMPERATURE: sb = new StringBuilder(); sb.append(當前溫度為:); sb.append(values[0]); etTemerature.setText(sb.toString()); break; // 光傳感器 case Sensor.TYPE_LIGHT: sb = new StringBuilder(); sb.append(當前光的強度為:); sb.append(values[0]); etLight.setText(sb.toString()); break; // 壓力傳感器 case Sensor.TYPE_PRESSURE: sb = new StringBuilder(); sb.append(當前壓力為:); sb.append(values[0]); etPressure.setText(sb.toString()); break; } } }
傳感器應用案例
對傳感器的支持是Android系統的特性之一,通過使用傳感器可以開發出各種有趣的應用,我們通過方向傳感器來開發指南針。
開發指南針的思路比較簡單:程序先准備一張指南針圖片,該圖片上方向指針指向北方。接下來開發一個檢測方向的傳感器,程序檢測到手機頂部繞Z軸轉過多少度,讓指南針圖片反向轉過多少度即可。
該應用中只要在界面中添加一張圖片,並讓圖片總是反向轉過方向傳感器反回的第一個角度即可。
例:指南針:
Compass.java
public class Compass extends Activity implements SensorEventListener { // 定義顯示指南針的圖片 ImageView znzImage; // 記錄指南針圖片轉過的角度 float currentDegree = 0f; // 定義模擬器的Sensor管理器 // private SensorManagerSimulator mSensorManager; // 定義真機的Sensor管理器 SensorManager mSensorManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 獲取界面中顯示指南針的圖片 znzImage = (ImageView) findViewById(R.id.znzImage); // 獲取真機的傳感器管理服務 mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); // // 獲取傳感器模擬器的傳感器管理服務 // mSensorManager = SensorManagerSimulator.getSystemService(this, // SENSOR_SERVICE); // // 連接傳感器模擬器 // mSensorManager.connectSimulator(); } @Override protected 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 onSensorChanged(SensorEvent event) { // 真機上獲取觸發event的傳感器類型 int sensorType = event.sensor.getType(); // // 模擬器上獲取觸發event的傳感器類型 // int sensorType = event.type; switch (sensorType) { case Sensor.TYPE_ORIENTATION: // 獲取繞Z軸轉過的角度。 float degree = event.values[0]; // 創建旋轉動畫(反向轉過degree度) RotateAnimation ra = new RotateAnimation(currentDegree, -degree, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); // 設置動畫的持續時間 ra.setDuration(200); // 運行動畫 znzImage.startAnimation(ra); currentDegree = -degree; break; } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }
main.xml
例:水平儀:
Gradienter.java
public class Gradienter extends Activity implements SensorEventListener { // 定義水平儀的儀表盤 MyView show; // 定義水平儀能處理的最大傾斜角,超過該角度,氣泡將直接在位於邊界。 int MAX_ANGLE = 30; // // 定義真機的Sensor管理器 // SensorManager mSensorManager; // 定義模擬器的Sensor管理器 SensorManagerSimulator 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); // 獲取傳感器模擬器的傳感器管理服務 mSensorManager = SensorManagerSimulator.getSystemService(this, SENSOR_SERVICE); // 連接傳感器模擬器 mSensorManager.connectSimulator(); } @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(); // 模擬器上獲取觸發event的傳感器類型 int sensorType = event.type; 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; } } }
MyView.java
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); } }
<framelayout android:background="#fff" android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"></framelayout>
Android 破解視頻App去除廣告功能作為一個屌絲程序猿也有追劇的時候,但是當打開視頻app的時候,那些超長的廣告已經讓我這個屌絲無法忍受了,作為一個程序猿看視頻還要
使用自定義Animation,實現View的左右搖擺效果,如圖所示:代碼很簡單,直接上源碼activity_maini.xml布局文件:<?xml vers
目前,各種App的社區或者用戶曬照片、發說說的地方,都提供了評論功能,為了更好地學習,自己把這個功能實現了一下,做了個小的Demo。首先推薦一款實用的插件LayoutCr
自繪控件的內容都是自己繪制出來的 大致流程如下:1.定義一個類繼承view 1.使用TypedArray初始化屬性集合 在