編輯:關於android開發
好久之前就已經研究了方向傳感器Sensor.TYPE_ORIENTATION。根據自已實踐,改寫了網上的兩個水准儀的例子,又重新封裝使用了一下,最後也用在了項目中。
下面這段話是出自Android 傳感器之方向傳感器
一般情況下,在android系統中獲取手機的方位信息在api中有TYPE_ORIENTATION常量,可以像得到加速度傳感器那樣得到方向傳感器sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);然而我們這樣做的話在最新版的SDK中就會看到這麼一句話:“TYPE_ORIENTATIONThis constant is deprecated. use SensorManager.getOrientation() instead. ”即這種方式也過期,不建議使用!Google建議我們在應用程序中使用SensorManager.getOrientation()來獲得原始數據。
public static float[] getOrientation (float[] R, float[] values)
第一個參數是R[] 是一個旋轉矩陣,用來保存磁場和加速度的數據,可以理解為這個函數的傳入值,通過它這個函數給你求出方位角。
第二個參數就是這個函數的輸出了,他有函數自動為我們填充,這就是我們想要的。
values[0]:方向角,但用(磁場+加速度)得到的數據范圍是(-180~180),也就是說,0表示正北,90表示正東,180/-180表示正南,-90表示正西。而直接通過方向感應器數據范圍是(0~359)360/0表示正北,90表示正東,180表示正南,270表示正西。
values[1]pitch 傾斜角即由靜止狀態開始,前後翻轉,手機頂部往上抬起(0~-90),手機尾部往上抬起(0~90)
values[2] roll旋轉角即由靜止狀態開始,左右翻轉,手機左側抬起(0~90),手機右側抬起(0~-90)
package com.level.level1; import com.level.level1.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; public class LevelView extends View { // 定義水平儀大圓盤圖片 Bitmap compass; // 定義水平儀中的氣泡圖標 Bitmap ball; // 定義水平儀中氣泡 的X、Y座標 int ballX, ballY; // 定義氣泡位於中間時(水平儀完全水平),氣泡的X、Y座標 int cx, cy; // 定義水平儀大圓盤中心座標X、Y int backCx; int backCy; // 定義靈敏度,即水平儀能處理的最大傾斜角,超過該角度,氣泡將直接在位於邊界。 int SENSITIVITY = 30; public LevelView(Context context, AttributeSet attrs) { super(context, attrs); // 加載水平儀大圓盤圖片和氣泡圖片 compass = BitmapFactory.decodeResource(getResources() , R.drawable.back); ball = BitmapFactory .decodeResource(getResources(), R.drawable.small); // 計算出 水平儀完全水平時 氣泡位置 左上角為原點 cx = (compass.getWidth() - ball.getWidth()) / 2; cy = (compass.getHeight() - ball.getHeight()) / 2; // 計算出水平儀大圓盤中心座標X、Y 左上角為原點 backCx = compass.getWidth() / 2; backCy = compass.getWidth() / 2; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 繪制水平儀大圓盤圖片 canvas.drawBitmap(compass, 0, 0, null); // 根據氣泡坐標繪制氣泡 canvas.drawBitmap(ball, ballX, ballY, null); } public void onChangeXY(int zAngle,int yAngle,int xAngle){ // 定義氣泡當前位置X Y坐標值 int x, y; x = cx; y = cy; // 如果沿x軸的傾斜角 在最大角度之內則計算出其相應坐標值 if (Math.abs(xAngle) <= SENSITIVITY) { // 根據與x軸的傾斜角度計算X座標的變化值(傾斜角度越大,X座標變化越大) int deltaX = (int) (cx * xAngle / SENSITIVITY); x += deltaX; } // 如果沿x軸的傾斜角已經大於MAX_ANGLE,氣泡應到最左邊 else if (xAngle > SENSITIVITY) { x = 0; } // 如果與x軸的傾斜角已經小於負的MAX_ANGLE,氣泡應到最右邊 else { x = cx * 2; } // 如果沿Y軸的傾斜角還在最大角度之內 if (Math.abs(yAngle) <= SENSITIVITY) { // 根據沿Y軸的傾斜角度計算Y座標的變化值(傾斜角度越大,Y座標變化越大) int deltaY = (int) (cy * yAngle / SENSITIVITY); y += deltaY; } // 如果沿Y軸的傾斜角已經大於MAX_ANGLE,氣泡應到最下邊 else if (yAngle > SENSITIVITY) { y = cy * 2; } // 如果沿Y軸的傾斜角已經小於負的MAX_ANGLE,氣泡應到最右邊 else { y = 0; } // 如果計算出來的X、Y座標還位於水平儀的儀表盤內,更新水平儀的氣泡座標 if (isContain(x, y)) { ballX = x; ballY = y; } else { // 有待後續繼續完成 } //重繪界面 invalidate(); } // 計算x、y點的氣泡是否處於水平儀的大圓盤內 private boolean isContain(int x, int y) { // 計算氣泡的圓心座標X、Y int ballCx = x + ball.getWidth() / 2; int ballCy = y + ball.getWidth() / 2; // 計算氣泡的圓心與水平儀大圓盤中中心之間的距離。 double distance = Math.sqrt((ballCx - backCx) * (ballCx - backCx) + (ballCy - backCy) * (ballCy - backCy)); // 若兩個圓心的距離小於它們的半徑差,即可認為處於該點的氣泡依然位於儀表盤內 if (distance < cx) { return true; } else { return false; } } }
package com.level.level1; import com.level.level1.R; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.app.Activity; import android.widget.TextView; public class MyGradienter extends Activity implements SensorEventListener { // 定義水平儀的儀大圓盤 private LevelView myview; // 定義Sensor管理器 SensorManager mySM; // 定義顯示欄 顯示X Y Z軸方向轉過角度與當前方位 private TextView tx, ty, tz, td; private Sensor acc_sensor; private Sensor mag_sensor; // 加速度傳感器數據 float accValues[] = new float[3]; // 地磁傳感器數據 float magValues[] = new float[3]; // 旋轉矩陣,用來保存磁場和加速度的數據 float r[] = new float[9]; // 模擬方向傳感器的數據(原始數據為弧度) float values[] = new float[3]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_gradienter); myview = (LevelView) findViewById(R.id.myview); tx = (TextView) findViewById(R.id.testviewx); ty = (TextView) findViewById(R.id.testviewy); tz = (TextView) findViewById(R.id.testviewz); td = (TextView) findViewById(R.id.testviewd); // 獲取手機傳感器管理服務 mySM = (SensorManager) getSystemService(SENSOR_SERVICE); } @Override public void onResume() { super.onResume(); acc_sensor = mySM.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mag_sensor = mySM.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); // 給傳感器注冊監聽: mySM.registerListener(this, acc_sensor, SensorManager.SENSOR_DELAY_GAME); mySM.registerListener(this, mag_sensor, SensorManager.SENSOR_DELAY_GAME); } @Override protected void onPause() { // 取消方向傳感器的監聽 mySM.unregisterListener(this); super.onPause(); } @Override protected void onStop() { // 取消方向傳感器的監聽 mySM.unregisterListener(this); super.onStop(); } @Override public void onAccuracyChanged(Sensor arg0, int arg1) { // TODO Auto-generated method stub } @Override public void onSensorChanged(SensorEvent event) { // 獲取手機觸發event的傳感器的類型 int sensorType = event.sensor.getType(); switch (sensorType) { case Sensor.TYPE_ACCELEROMETER: accValues = event.values.clone(); break; case Sensor.TYPE_MAGNETIC_FIELD: magValues = event.values.clone(); break; } SensorManager.getRotationMatrix(r, null, accValues, magValues); SensorManager.getOrientation(r, values); // 獲取 沿著Z軸轉過的角度 int zAngle = (int) Math.toDegrees(values[0]); tz.setText("Z軸方向轉過的角度:" + zAngle); // 顯示當前的方位 displayCompass(zAngle); // 獲取 沿著X軸傾斜時 與Y軸的夾角 int yAngle = (int) Math.toDegrees(values[1]); ty.setText("Y軸方向翹起的角度:" + yAngle); // 獲取 沿著Y軸的滾動時 與X軸的角度 int xAngle = (int) Math.toDegrees(values[2]); tx.setText("x軸方向翹起的角度:" + xAngle); myview.onChangeXY(zAngle,yAngle,xAngle); } private void displayCompass(int angle) { if ((angle < 22.5) || (angle > 337.5)) td.setText("手機頂部當前方位: 北"); if ((angle > 22.5) && (angle < 67.5)) td.setText("手機頂部當前方位: 西北"); if ((angle > 67.5) && (angle < 112.5)) td.setText("手機頂部當前方位: 西"); if ((angle > 112.5) && (angle < 157.5)) td.setText("手機頂部當前方位: 西北"); if ((angle > 157.5) && (angle < 202.5)) td.setText("手機頂部當前方位: 南"); if ((angle > 202.5) && (angle < 247.5)) td.setText("手機頂部當前方位: 東南"); if ((angle > 247.5) && (angle < 292.5)) td.setText("手機頂部當前方位: 東"); if ((angle > 292.5) && (angle < 337.5)) td.setText("手機頂部當前方位: 東北"); } }
package com.tcjt.level2; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; public class MainView extends View { Paint paint = new Paint(); //畫筆 Bitmap shangBitmap1; //上面的大矩形圖 Bitmap shangBitmap2; //上面的氣泡 Bitmap zuoBitmap1; //左面的大矩形圖 Bitmap zuoBitmap2; //左面圖的氣泡 Bitmap zhongBitmap1; //中間的大圓圖 Bitmap zhongBitmap2; //中間的小氣泡 Bitmap xiaBitmap1; //右下的矩形圖 Bitmap xiaBitmap2; //右下的氣泡 //背景矩形的位置聲明 int shang1_X = 60; //上面的大矩形圖 int shang1_Y = 12; int zuo1_X = 12; //左面的大矩形圖 int zuo1_Y = 60; int zhong1_X = 65; //中間的大圓圖 int zhong1_Y = 65; int xia1_X = 145; //右下的矩形圖 int xia1_Y = 145;//水泡的位置聲明 int shang2_X; //上面的氣泡XY 坐標 int shang2_Y; int zuo2_X; //左面圖的氣泡XY 坐標 int zuo2_Y; int zhong2_X; //中間的小氣泡XY 坐標 int zhong2_Y; int xia2_X; //右下的氣泡XY 坐標 int xia2_Y; public MainView(Context context, AttributeSet attrs){ super(context, attrs); initBitmap(); //初始化圖片資源 initLocation(); //初始化氣泡的位置 } private void initBitmap(){ //初始化圖片的方法 //該處省略了部分代碼,將在後面進行介紹 shangBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_shang1); shangBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_shang2); zuoBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zuo1); zuoBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zuo2); zhongBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zhong1); zhongBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zhong2); xiaBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_xia1); xiaBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_xia2); } private void initLocation(){ //初始化氣泡位置的方法 //該處省略了部分代碼,將在後面進行介紹 shang2_X = shang1_X + shangBitmap1.getWidth()/2- shangBitmap2.getWidth()/2; shang2_Y = shang1_Y + shangBitmap1.getHeight()/2- shangBitmap2.getHeight()/2; zuo2_X = zuo1_X + zuoBitmap1.getWidth()/2- zuoBitmap2.getWidth()/2; zuo2_Y = zuo1_Y + zuoBitmap1.getHeight()/2- zuoBitmap2.getHeight()/2; zhong2_X = zhong1_X + zhongBitmap1.getWidth()/2- zhongBitmap2.getWidth()/2; zhong2_Y = zhong1_Y + zhongBitmap1.getHeight()/2- zhongBitmap2.getHeight()/2; xia2_X = xia1_X + xiaBitmap1.getWidth()/2- xiaBitmap2.getWidth()/2; xia2_Y = xia1_Y + xiaBitmap1.getHeight()/2- xiaBitmap2.getHeight()/2; } @Override protected void onDraw(Canvas canvas){//重寫的繪制方法 super.onDraw(canvas); //該處省略了部分代碼,將在後面進行介紹 canvas.drawColor(Color.WHITE); //設置背景色為白色 paint.setColor(Color.BLUE); //設置畫筆顏色 paint.setStyle(Style.STROKE); //設置畫筆為不填充 canvas.drawRect(5, 5, 315, 315, paint);//繪制外邊框矩形 //畫背景矩形 canvas.drawBitmap(shangBitmap1, shang1_X,shang1_Y, paint); //上 canvas.drawBitmap(zuoBitmap1, zuo1_X,zuo1_Y, paint); //左 canvas.drawBitmap(zhongBitmap1, zhong1_X,zhong1_Y, paint); //中 canvas.drawBitmap(xiaBitmap1, xia1_X,xia1_Y, paint); //下 //開始繪制氣泡 canvas.drawBitmap(shangBitmap2, shang2_X,shang2_Y, paint); //上 canvas.drawBitmap(zuoBitmap2, zuo2_X,zuo2_Y, paint); //左 canvas.drawBitmap(zhongBitmap2, zhong2_X,zhong2_Y, paint); //中 canvas.drawBitmap(xiaBitmap2, xia2_X, xia2_Y, paint);//下 paint.setColor(Color.GRAY);//設置畫筆顏色用來繪制刻度 //繪制上面方框中的刻度 canvas.drawLine (shang1_X+shangBitmap1.getWidth()/2-7,shang1_Y, shang1_X+shangBitmap1.getWidth()/2-7,shang1_Y+shangBitmap1.getHeight()-2, paint); canvas.drawLine (shang1_X+shangBitmap1.getWidth()/2+7,shang1_Y, shang1_X+shangBitmap1.getWidth()/2+7,shang1_Y+shangBitmap1.getHeight()-2, paint); //繪制左面方框中的刻度 canvas.drawLine(zuo1_X,zuo1_Y+zuoBitmap1.getHeight()/2-7,zuo1_X+zuoBitmap1.getWidth()-2,zuo1_Y+zuoBitmap1.getHeight()/2-7, paint);canvas.drawLine(zuo1_X,zuo1_Y+zuoBitmap1.getHeight()/2+7,zuo1_X+zuoBitmap1.getWidth()-2,zuo1_Y+zuoBitmap1.getHeight()/2+7, paint); //繪制下面方框中的刻度 canvas.drawLine(xia1_X+xiaBitmap1.getWidth()/2-10,xia1_Y+xiaBitmap1.getHeight()/2-20,xia1_X+xiaBitmap1.getWidth()/2+20,xia1_Y+xiaBitmap1.getHeight()/2+10, paint); canvas.drawLine(xia1_X+xiaBitmap1.getWidth()/2-20,xia1_Y+xiaBitmap1.getHeight()/2-10,xia1_X+xiaBitmap1.getWidth()/2+10,xia1_Y+xiaBitmap1.getHeight()/2+20, paint); //中間圓圈中的刻度(小圓) RectF oval = new RectF(zhong1_X+zhongBitmap1.getWidth()/2-10,zhong1_Y+zhongBitmap1.getHeight()/2-10,zhong1_X+zhongBitmap1.getWidth()/2+10,zhong1_Y+zhongBitmap1.getHeight()/2+10); canvas.drawOval(oval, paint);//繪制基准線(圓) } }
package com.tcjt.level2; import android.hardware.SensorListener; import android.hardware.SensorManager; import android.os.Bundle; import android.app.Activity; import android.view.Menu; @SuppressWarnings("deprecation") public class LevelActivity extends Activity { int k = 45; //靈敏度 MainView mv; //真機 SensorManager mySensorManager; //測試時 // SensorManagerSimulator mySensorManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_level); mv = (MainView) findViewById(R.id.mainView); mySensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);//真機 } private final SensorListener mSensorLisener =new SensorListener(){ @Override public void onAccuracyChanged(int sensor, int accuracy) { } public boolean isContain(int x, int y){//判斷點是否在圓內 int tempx =(int) (x + mv.zhongBitmap2.getWidth()/2.0); int tempy =(int) (y + mv.zhongBitmap2.getWidth()/2.0); int ox = (int) (mv.zhong1_X+ mv.zhongBitmap1.getWidth()/2.0); int oy = (int) (mv.zhong1_X+ mv.zhongBitmap1.getWidth()/2.0); if(Math.sqrt((tempx-ox)*(tempx-ox)+(tempy-oy)*(tempy-oy))>(mv.zhongBitmap1.getWidth()/2.0-mv.zhongBitmap2.getWidth()/2.0)){ //不在圓內 return false; }else{ //在圓內時 return true; } } @Override public void onSensorChanged(int sensor, float[] values) { if(sensor == SensorManager.SENSOR_ORIENTATION){ double pitch = values[SensorManager.DATA_Y]; double roll = values[SensorManager.DATA_Z]; int x=0; int y=0;//臨時變量,算中間水泡坐標時用 int tempX=0; int tempY=0;//下面氣泡的臨時變量 //開始調整x 的值 if(Math.abs(roll)<=k){ mv.shang2_X = mv.shang1_X //上面的 + (int)(((mv.shangBitmap1.getWidth() -mv.shangBitmap2.getWidth())/2.0) -(((mv.shangBitmap1.getWidth() -mv.shangBitmap2.getWidth())/2.0)*roll)/k); x = mv.zhong1_X //中間的 + (int)(((mv.zhongBitmap1.getWidth() -mv.zhongBitmap2.getWidth())/2.0) -(((mv.zhongBitmap1.getWidth() -mv.zhongBitmap2.getWidth())/2.0)*roll)/k); }else if(roll>k){ mv.shang2_X=mv.shang1_X; x = mv.zhong1_X; }else{ mv.shang2_X=mv.shang1_X+ mv.shangBitmap1.getWidth() - mv.shangBitmap2.getWidth(); x = mv.zhong1_X+ mv.zhongBitmap1.getWidth() - mv.zhongBitmap2.getWidth(); } //開始調整y 的值 if(Math.abs(pitch)<=k){ mv.zuo2_Y=mv.zuo1_Y //左面的 + (int)(((mv.zuoBitmap1.getHeight() -mv.zuoBitmap2.getHeight())/2.0) +(((mv.zuoBitmap1.getHeight() -mv.zuoBitmap2.getHeight())/2.0)*pitch)/k); y =mv.zhong1_Y+ //中間的 (int)(((mv.zhongBitmap1.getHeight() -mv.zhongBitmap2.getHeight())/2.0) +(((mv.zhongBitmap1.getHeight() -mv.zhongBitmap2.getHeight())/2.0)*pitch)/k); }else if(pitch>k){ mv.zuo2_Y=mv.zuo1_Y +mv.zuoBitmap1.getHeight() -mv.zuoBitmap2.getHeight(); y=mv.zhong1_Y+mv.zhongBitmap1.getHeight() -mv.zhongBitmap2.getHeight(); }else{ mv.zuo2_Y = mv.zuo1_Y; y = mv.zhong1_Y; } //下面的 tempX = -(int) (((mv.xiaBitmap1.getWidth()/2-28)*roll +(mv.xiaBitmap1.getWidth()/2-28)*pitch)/k); tempY = -(int) ((-(mv.xiaBitmap1.getWidth()/2-28)*roll -(mv.xiaBitmap1.getWidth()/2-28)*pitch)/k); //限制下面的氣泡范圍 if(tempY>mv.xiaBitmap1.getHeight()/2-28){ tempY = mv.xiaBitmap1.getHeight()/2-28; } if(tempY < -mv.xiaBitmap1.getHeight()/2+28){ tempY = -mv.xiaBitmap1.getHeight()/2+28; } if(tempX > mv.xiaBitmap1.getWidth()/2-28){ tempX = mv.xiaBitmap1.getWidth()/2-28; } if(tempX < -mv.xiaBitmap1.getWidth()/2+28){ tempX = -mv.xiaBitmap1.getWidth()/2+28; } mv.xia2_X = tempX + mv.xia1_X + mv.xiaBitmap1.getWidth()/2 -mv.xiaBitmap2.getWidth()/2; mv.xia2_Y = tempY + mv.xia1_Y + mv.xiaBitmap1.getHeight()/2 - mv.xiaBitmap2.getWidth()/2; if(isContain(x, y)){//中間的水泡在圓內才改變坐標 mv.zhong2_X = x; mv.zhong2_Y = y; } mv.postInvalidate();//重繪MainView } } //傳感器監聽器類 //該處省略了部分代碼,將在後面進行介紹 }; @Override protected void onResume(){ //添加監聽 mySensorManager.registerListener(mSensorLisener,SensorManager.SENSOR_ORIENTATION); super.onResume(); } @Override protected void onPause() { //取消監聽 mySensorManager.unregisterListener (mSensorLisener); super.onPause(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_level, menu); return true; } }兩個例子都能實現水准儀的效果。圖片我就不貼,可以參考裡面的代碼。
Activity啟動模式之SingleInstance,singleinstance模式 終於到了最後一種啟動模式了,指定為singleInstance模式的活動會啟用
Android中Activity處理返回結果的實現方式,androidactivity大家在網上購物時都有這樣一個體驗,在確認訂單選擇收貨人以及地址時,會跳轉頁面到我們存
Android 內存洩漏的幾種可能總結 Java是垃圾回收語言的一種,其優點是開發者無需特意管理內存分配,降低了應用由於局部故障(segmentation
[android] 手機衛士黑名單功能(列表展示),android衛士先把要攔截的電話號碼保存到數據庫中,攔截模式用個字段區分,1 電話攔截,2 短信攔截,3全部攔截 &