android 有很多传感器,可以利用传感器实现很多新奇的功能,在这里我们实现水平仪的功能,主要是利用方向传感器就可以了。
1.android 的坐标系是如何定义x, y z 轴的。
x轴的方向是沿着屏幕的水平方向从左向右,如果手机不是正方形的话,较短的边需要水平放置,较长的边需要垂直放置。
Y轴的方向是从屏幕的左下角开始沿着屏幕的的垂直方向指向屏幕的顶端。
将手机放在桌子上,z轴的方向是从手机指向天空。
2.方向传感器
在方向传感器中values变量的3个值都表示度数,它们的含义如下:
values[0]:该值表示方位,也就是手机绕着Z轴旋转的角度。0表示北(North);90表示东(East);180表示南(South);270表示西(West)。如果values[0]的值正好是这4个值,并且手机是水平放置,表示手机的正前方就是这4个方向。
values[1]:该值表示倾斜度,或手机翘起的程度。当手机绕着X轴倾斜时该值发生变化。values[1]的取值范围是-180≤values[1]≤180。假设将手机屏幕朝上水平放在桌子上,这时如果桌子是完全水平的,values[1]的值应该是0(由于很少有桌子是绝对水平的,因此,该值很可能不为0,但一般都是-5和5之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。在这个旋转过程中,values[1]会在0到-180之间变化,也就是说,从手机顶部抬起时,values[1]的值会逐渐变小,直到等于-180。如果从手机底部开始抬起,直到将手机沿X轴旋转180度,这时values[1]会在0到180之间变化。也就是values[1]的值会逐渐增大,直到等于180。
values[2]:表示手机沿着Y轴的滚动角度。取值范围是-90≤values[2]≤90。假设将手机屏幕朝上水平放在桌面上,这时如果桌面是平的,values[2]的值应为0。将手机左侧逐渐抬起时,values[2]的值逐渐变小,直到手机垂直于桌面放置,这时values[2]的值是-90。将手机右侧逐渐抬起时,values[2]的值逐渐增大,直到手机垂直于桌面放置,这时values[2]的值是90。在垂直位置时继续向右或向左滚动,values[2]的值会继续在-90至90之间变化。
笔者在实现水平仪的过程中发现一个问题,由于笔者只有摩托defy手机一部,搭载的是android 4.0系统,发现需要注册方向传感器、加速度传感器、磁场传感器(可能不需要),而不是大多说网上说的只注册方向传感器就可以了,只注册方向传感器只能获取到values[0],values[1]、values[2]不能正常获取的到。
现在这个水平仪还有一个问题就是水泡有些闪烁的厉害,基本功能已经实现了。有人改进了可以告诉一下我。
1.自定义LevelView.java,这个ImageView就是自定义水平仪的表盘和水泡View
1 package com.example.level; 2 import android.content.Context; 3 import android.graphics.Bitmap; 4 import android.graphics.BitmapFactory; 5 import android.graphics.Canvas; 6 import android.util.AttributeSet; 7 import android.widget.ImageView; 8 9 public class LevelView extends ImageView{ 10 //定义水平仪的表盘图片 11 Bitmap back_c; 12 //定义水平仪的气泡图片 13 Bitmap bubble_l; 14 //定义圆形水平仪中气泡x、y的坐标 15 int bubbleX_c,bubbleY_c; 16 public LevelView(Context context, AttributeSet attrs) { 17 super(context, attrs); 18 // TODO Auto-generated constructor stub 19 //加载水平仪图片和气泡图片 20 back_c=BitmapFactory.decodeResource(getResources(), R.drawable.level); 21 bubble_l=BitmapFactory.decodeResource(getResources(), R.drawable.ball_l); 22 } 23 @Override 24 protected void onDraw(Canvas canvas){ 25 super.onDraw(canvas); 26 canvas.drawBitmap(back_c,(getWidth()-back_c.getWidth())/2, 27 (getHeight()-back_c.getHeight())/2,null); 28 canvas.drawBitmap(bubble_l,(getWidth()-back_c.getWidth())/2+bubbleX_c, 29 (getHeight()-back_c.getHeight())/2+bubbleY_c,null); 30 } 31 }
2.主Activity,MainActivity.java
1 package com.example.level; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.hardware.Sensor; 6 import android.hardware.SensorEvent; 7 import android.hardware.SensorEventListener; 8 import android.hardware.SensorManager; 9 import android.os.Bundle; 10 import android.os.Handler; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.Button; 14 15 public class MainActivity extends Activity implements SensorEventListener{ 16 private Button level_btn_back;//返回按钮 17 LevelView lv; 18 private SensorManager mSensorManager; 19 protected final Handler mHandler = new Handler(); 20 float yAngle,zAngle; 21 // 这个是更新水平仪的线程,handler的灵活使用 22 protected Runnable mLevelViewUpdater = new Runnable() { 23 @Override 24 public void run() { 25 if (lv!= null){ 26 updateLevelViewBubble(); 27 mHandler.postDelayed(mLevelViewUpdater, 50);//50毫米后重新执行自己 28 } 29 } 30 }; 31 @Override 32 public void onCreate(Bundle b){ 33 super.onCreate(b); 34 setContentView(R.layout.activity_main); 35 level_btn_back=(Button)findViewById(R.id.level_btn_back); 36 lv=(LevelView)findViewById(R.id.levelview); 37 38 level_btn_back.setOnClickListener(new OnClickListener(){ 39 @Override 40 public void onClick(View v){ 41 MainActivity.this.finish(); 42 } 43 }); 44 //获取传感器服务 45 mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); 46 } 47 @Override 48 public void onResume(){ 49 super.onResume(); 50 /** 方向传感器需要使用加速度传感器和磁场传感器(这个可以不需要),不然的话获取y、z的数据不能变化,这里必须注册以下传感器 51 * android 4.0系统摩托罗拉 defy 测试至少需要加速度传感器才可以正常获取y、z的数据 52 * */ 53 mSensorManager.registerListener(this, mSensorManager 54 .getDefaultSensor(Sensor.TYPE_ORIENTATION), 55 SensorManager.SENSOR_DELAY_GAME); //为传感器注册监听器 56 mSensorManager.registerListener(this, 57 mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), 58 SensorManager.SENSOR_DELAY_GAME); 59 mSensorManager.registerListener(this, 60 mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), 61 SensorManager.SENSOR_DELAY_GAME); 62 mHandler.postDelayed(mLevelViewUpdater, 50); 63 } 64 @Override 65 protected void onStop(){ 66 //取消注册 67 mSensorManager.unregisterListener(this); 68 super.onStop(); 69 } 70 @Override 71 protected void onPause(){ 72 mSensorManager.unregisterListener(this); 73 super.onPause(); 74 } 75 private void updateLevelViewBubble(){ 76 //气泡位于圆盘中央时,气泡X,Y的坐标 77 int c_x=(lv.back_c.getWidth()-30-lv.bubble_l.getWidth())/2; 78 int c_y=(lv.back_c.getHeight()-30-lv.bubble_l.getHeight())/2; 79 //如果与Z轴的倾角还在最大角度之内 80 if(Math.abs(zAngle)<=30){ 81 int c_deltaX=(int)(((lv.back_c.getWidth()-30-lv.bubble_l 82 .getWidth())/2)*zAngle/30); 83 c_x+=c_deltaX; 84 }else if(zAngle>30){ 85 c_x=lv.back_c.getWidth()-30-lv.bubble_l.getWidth(); 86 }else{ 87 c_x=15; 88 } 89 //如果与Y轴的倾角还在最大角度之内 90 if(Math.abs(yAngle)<=30){ 91 int c_deltaY=(int)(((lv.back_c.getHeight()-30-lv.bubble_l 92 .getHeight())/2)*yAngle/30); 93 c_y+=c_deltaY; 94 }else if(yAngle>30){ 95 c_y=lv.back_c.getHeight()-30-lv.bubble_l.getHeight(); 96 }else{ 97 c_y=15; 98 } 99 if(isContain(c_x,c_y)){ 100 lv.bubbleX_c=c_x; 101 lv.bubbleY_c=c_y; 102 //System.out.println("X:"+c_x+"|Y:"+c_y); 103 //通知系统重绘LevelView组件 104 lv.postInvalidate(); 105 } 106 } 107 private boolean isContain(int x,int y){ 108 //计算气泡圆心坐标 109 int bubbleCx=x+lv.bubble_l.getWidth()/2; 110 int bubbleCy=y+lv.bubble_l.getHeight()/2; 111 //仪表盘圆心坐标 112 int backCx=(lv.back_c.getWidth()-30)/2; 113 int backCy=(lv.back_c.getHeight()-30)/2; 114 //计算气泡的圆心与水平仪表盘圆心之间的距离 115 double distance=Math.sqrt((bubbleCx-backCx)*(bubbleCx-backCx)+ 116 (bubbleCy-backCy)*(bubbleCy-backCy)); 117 //若两个圆心的距离小于他们的半径差,即认为气泡位于仪表盘中 118 if(distance<(lv.back_c.getWidth()-lv.bubble_l.getWidth())/2){ 119 return true; 120 }else{ 121 return false; 122 } 123 } 124 //必须实现的两个方法 125 @Override 126 public void onAccuracyChanged(Sensor sensor, int accuracy) { 127 // TODO Auto-generated method stub 128 129 } 130 @Override 131 public void onSensorChanged(SensorEvent event) { 132 // TODO Auto-generated method stub 133 float[] values=event.values; 134 //获取与Y轴夹角 135 yAngle=values[1]; 136 //获取与Z轴夹角 137 zAngle=values[2]; 138 //System.out.println("yAngle:"+yAngle+"|zAngle"+zAngle); 139 } 140 }
3.XML文件,activity_main.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" 6 android:background="@drawable/common_background" > 7 <RelativeLayout 8 android:layout_width="fill_parent" 9 android:layout_height="wrap_content" 10 android:background="@drawable/top_title_bg" 11 android:gravity="center"> 12 <Button 13 android:id="@+id/level_btn_back" 14 android:layout_width="50dp" 15 android:layout_height="30dp" 16 android:background="@drawable/btn_back_style" 17 android:text="后退" 18 android:textColor="#F5F5DC" 19 android:textSize="17sp" 20 android:layout_alignParentLeft="true" 21 android:gravity="center" 22 android:paddingLeft="10dp" 23 android:paddingRight="3dp" 24 android:paddingTop="1dp" 25 android:paddingBottom="3dp" 26 android:layout_marginLeft="6dp"/> 27 <TextView 28 android:id="@+id/level_textview_title" 29 android:layout_width="fill_parent" 30 android:layout_height="wrap_content" 31 android:text="水平仪" 32 android:textColor="#F5F5DC" 33 android:textSize="20sp" 34 android:gravity="center"/> 35 </RelativeLayout> 36 <com.example.level.LevelView 37 android:id="@+id/levelview" 38 android:layout_width="fill_parent" 39 android:layout_height="fill_parent" /> 40 </LinearLayout>
浙公网安备 33010602011771号