編輯:關於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模式
Activity啟動模式之SingleInstance,singleinstance模式 終於到了最後一種啟動模式了,指定為singleInstance模式的活動會啟用
Android中Activity處理返回結果的實現方式,androidactivity
Android中Activity處理返回結果的實現方式,androidactivity大家在網上購物時都有這樣一個體驗,在確認訂單選擇收貨人以及地址時,會跳轉頁面到我們存
Android 內存洩漏的幾種可能總結
Android 內存洩漏的幾種可能總結 Java是垃圾回收語言的一種,其優點是開發者無需特意管理內存分配,降低了應用由於局部故障(segmentation
[android] 手機衛士黑名單功能(列表展示),android衛士
[android] 手機衛士黑名單功能(列表展示),android衛士先把要攔截的電話號碼保存到數據庫中,攔截模式用個字段區分,1 電話攔截,2 短信攔截,3全部攔截 &