基于Android嵌入式平台传感器应用开发水平仪
扫描二维码
随时随地手机看文章
摘要:详细介绍了如何利用Android系统的传感器开发水平仪应用的全过程。通过对该案例开发的讲解,介绍了传感器应用的开发方法以及通过Eclipse开发Android应用程序的过程。
1Android平台简介
互联网巨头Google公司于2007年11月5日推出了全新的嵌入式软件平台---Android,该平台由操作系统、中间件、用户界面以及应用软件组成,是一个真正开放的移动应用开发平台。
2007年11月初,Google与其他33家手机厂商、软硬件供应商、手机芯片供应商、移动运营商联合组成了开放手机联盟(OpenHandsetAlliance),发布了名为Android的手机软件平台,并宣布该平台完全开放。同时Google希望Android平台成为一套标准化、开放式的移动嵌入式软件平台。
由于Android系统具有开发性、平等性、无界性以及方便性等优点,所以很快被业界所接受。从2008年初开始,越来越多的开发人员投身到Android应用的开发当中。
而Android系统的一大亮点之一就是传感器的使用,利用传感器可以开发出很多新奇有趣的应用程序。例如计步器、水平仪,甚至在很多游戏中都可以使用传感器来操作游戏。传感器的种类有很多种,其中包括加速度传感器、姿态传感器、磁场传感器、温度传感器以及光传感器等,介绍的水平仪应用就是使用姿态传感器的。
2案例功能
将结合水平仪案例的开发详细介绍如何在Android平台下开发传感器应用,下面首先对水平仪的功能及界面进行简单的介绍。
2。1程序界面
程序运行后的效果如图1所示,用户可以通过调整手机的姿态来控制界面中各个气泡的位置。与真正的水平仪一样,在使用手机水平仪时,需要将手机平放到某个平面上才可以。
图1 水平仪应用程序界面
2。2软件功能 运行该程序,当改变手机的姿态时,界面中的气泡便会根据手机的姿态向高处进行相应的移动。 当手机所处的平面水平时,各个气泡都应该位于中间的指定区域。 3开发环境搭建 正式进入代码开发之前,首先需要对开发环境进行搭建,其搭建步骤如下所列。 (1)安装Java开发环境JDK。 (2)从网上下载Android开发环境SDK的压缩包,并将其解压到磁盘上的某个位置。 (3)将SDK解压目录中的tools目录添加到系统的PATH环境变量中。 (4)下载并安装Eclipse集成开发环境。 (5)为Eclipse安装Android开发插件ADT,并在Eclipse的Preferences中配置Android插件的SDKLocation。 (6)在Eclipse的AVDManager中创建Android虚拟设备(AVD),并启动模拟器。 (7)下载并安装用来调试Android传感器应用的Sensorsimulator传感器模拟器软件。 (8)在模拟器中安装Sensorsimulator所对应的apk文件并对其进行调试使Sensorsimulator应用程序能够与Android模拟器进行通信。 4开发前的准备 前面完成了开发环境的搭建,但在正式进行代码开发之前,还需要再做一些开发前的准备工作,其步骤如下: (1)首先启动之前安装好Eclipse。 (2)然后依次点击File|New|Other|Android|AndroidProject进入项目的创建界面。 (3)在项目创建界面中,输入项目的名称、所使用的目标平台、所在的包名等信息,如图2所示。
图2 在Eclipse 中创建Android 项目
(4)点击"Finish"完成项目的创建。 (5)在程序中将会用到的图片资源存放到项目文件夹的res/drawable-mdpi目录下,如图3所示。 图3 图片资源 (6)为应用程序引入调试时使用的Sensorsimulator支持jar包,该jar包位于Sensorsimulator安装目录中的bin目录下: 5自定义View的开发 本案例需要自定义一个View来绘制水平仪的用户界面,首先需要在项目文件夹的src/wyf/ytl目录下创建一个名为Main-View的java类,并使其继承自View类,其代码框架如下: packagewyf。ytl;//声明所在包 importandroid。content。Context;//引入Context类 importandroid。graphics。Bitmap;//引入Bitmap类 importandroid。graphics。BitmapFactory;//引入相关类 importandroid。graphics。Canvas;//引入Canvas类 importandroid。graphics。Color;//引入Color类 importandroid。graphics。Paint;//引入Paint类 importandroid。graphics。RectF;//引入RectF类 importandroid。graphics。Paint。Style;//引入Style类 importandroid。util。AttributeSet;//引入AttributeSet类 importandroid。view。View;//引入View类 publicclassMainViewextendsView{ Paintpaint=newPaint();//画笔 //图片资源的声明 BitmapshangBitmap1;//上面的大矩形图 BitmapshangBitmap2;//上面的气泡 BitmapzuoBitmap1;//左面的大矩形图 BitmapzuoBitmap2;//左面图的气泡 BitmapzhongBitmap1;//中间的大圆图 BitmapzhongBitmap2;//中间的小气泡 BitmapxiaBitmap1;//右下的矩形图[!--empirenews.page--] BitmapxiaBitmap2;//右下的气泡 //背景矩形的位置声明 intshang1_X=60;//上面的大矩形图 intshang1_Y=12; intzuo1_X=12;//左面的大矩形图 intzuo1_Y=60; intzhong1_X=65;//中间的大圆图 intzhong1_Y=65; intxia1_X=145;//右下的矩形图 intxia1_Y=145;//水泡的位置声明 intshang2_X;//上面的气泡XY坐标 intshang2_Y; intzuo2_X;//左面图的气泡XY坐标 intzuo2_Y; intzhong2_X;//中间的小气泡XY坐标 intzhong2_Y; intxia2_X;//右下的气泡XY坐标 intxia2_Y; publicMainView(Contextcontext,AttributeSetattrs){ super(context,attrs); initBitmap();//初始化图片资源 initLocation();//初始化气泡的位置 } privatevoidinitBitmap(){//初始化图片的方法 …//该处省略了部分代码,将在后面进行介绍 } privatevoidinitLocation(){//初始化气泡位置的方法 …//该处省略了部分代码,将在后面进行介绍 } @Override protectedvoidonDraw(Canvascanvas){//重写的绘制方法 super。onDraw(canvas); …//该处省略了部分代码,将在后面进行介绍 } } 上述代码中的initBitmap以及initLocation方法是对界面进行初始化方法,而onDraw方法会根据需要绘制整个界面。 MainView类构造器中调用了两个单独的方法对整个界面进行了初始化,这是一种非常好的编程习惯。因为把不同功能的代码各自编写成独立的方法可以使主逻辑清晰,且各个方法中的代码都不是很长,会大大提高代码的可读性以及可维护性。 完成了代码框架的开发后就可以对其中各个方法进行开发了,首先开发的是图片资源的初始化方法,其代码如下: privatevoidinitBitmap(){//初始化图片资源的方法 shangBitmap1=BitmapFactory。decodeResource(getResources(),R。drawable。shang1); shangBitmap2=BitmapFactory。decodeResource(getResources(),R。drawable。shang2); zuoBitmap1=BitmapFactory。decodeResource(getResources(),R。drawable。zuo1); zuoBitmap2=BitmapFactory。decodeResource(getResources(),R。drawable。zuo2); zhongBitmap1=BitmapFactory。decodeResource(getResources(),R。drawable。zhong1); zhongBitmap2=BitmapFactory。decodeResource(getResources(),R。drawable。zhong2); xiaBitmap1=BitmapFactory。decodeResource(getResources(),R。drawable。xia1); xiaBitmap2=BitmapFactory。decodeResource(getResources(),R。drawable。xia2); }
上述代码为initBitmap方法的全部代码,其作用是对程序中所有的图片资源进行初始化,在开发该方法之前,应该确保所有的图片资源已经存放到了指定的目录下。
完成了图片资源初始化方法的开发后,便可对气泡位置初始化方法initLocation进行开发了,其代码如下:
privatevoidinitLocation(){//初始化气泡位置的方法
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;
}
在该方法中通过相应图片的宽度和高度动态计算气泡的初始坐标,采用此方法动态计算气泡坐标的好处是当日后更改图片资源后,不需要重写修改源代码即可正常运行,大大提高了程序的可维护性。
在完成了各个初始化方法的开发后就可以对绘制方法onDraw进行开发,该方法主要负责界面的绘制工作,其代码如下:
@Override
protectedvoidonDraw(Canvascanvas){//界面绘制方法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);[!--empirenews.page--]
//绘制左面方框中的刻度
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);
//中间圆圈中的刻度(小圆)
RectFoval=newRectF(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);//绘制基准线(圆)
}
在该方法中,根据相应图片的X、Y坐标将图片绘制到屏幕中,在图片的绘制过程中,同样动态根据相应图片的宽和高计算需要绘制到的位置坐标,以提高程序的可维护性与灵活性。
6相关XML文件的编写
完成了用于显示水平仪界面的自定义View的Java代码开发之后,就应该对布局XML资源文件进行编写,以将之前开发的自定义View添加到用户界面中。打开项目中res/layout目录下的main。xml,在其中编写如下的xml代码:
<?xmlversion="1。0"encoding="utf-8"?><!--编码格式-->
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"><!--线性布局-->
android:id="@+id/mainView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/><!--自定义
View-->
</LinearLayout>
编写完布局文件main。xml后,还需要开发字符串资源文件strings。xml。打开res/values下的strings。xml文件,编写如下的代码: <?xmlversion="1。0"encoding="utf-8"?><!--编码方式--> 水平仪 在该文件中只是对字符串app_name进行了定义,在开发Android应用程序时,将字符串资源统一定义到一个xml文件中是一个很好的编程习惯。 编写完上述的xml资源文件后,为了调试还需要为此应用程序添加网络权限,打开项目根目录下的AndroidManifest。xml文件,在""标签之前加入下列代码: 上述代码的功能为此应用程序添加了访问网络的权限。 7Activity类的开发 完成了自定义View以及XML文件的开发后,就可以对用户界面对应的Activity类进行开发,首先开发该类的代码框架,其代码如下: packagewyf。ytl;//声明所在包 importandroid。app。Activity;//引入相关类 importandroid。hardware。SensorListener; importandroid。hardware。SensorManager; importandroid。os。Bundle; publicclassSPYActivityextendsActivity{//继承ActivityMainViewmv;//主View intk=45;//灵敏度 //SensorManagermySensorManager; //真机 SensorManagerSimulatormySensorManager;//测试时@Override publicvoidonCreate(BundlesavedInstanceState){super。onCreate(savedInstanceState); setContentView(R。layout。main);//设置当前用户界面 mv=(MainView)findViewById(R。id。mainView); mySensorManager=SensorManagerSimulator。getSystemService(this,SENSOR_SERVICE);//测试时 mySensorManager。connectSimulator();//测试时 //mySensorManager=(SensorManager) //getSystemService(SENSOR_SERVICE);//真机 } privatefinalSensorListenermSensorLisener=newSensorListener(){//传感器监听 //器类 …//该处省略了部分代码,将在后面进行介绍 }; @Override protectedvoidonResume(){//添加监听 mySensorManager。registerListener(mSensorLisener,SensorManager。SENSOR_ORIENTATION); super。onResume(); } @Override protectedvoidonPause(){//取消监听 mySensorManager。unregisterListener(mSensorLisener); super。onPause(); } } 上述代码中除了重写了onCreate方法外,还重写了onRe-sume以及onPause方法为mySensorManager添加或删除监听,并且定义了传感器监听器类mSensorLisener。 在完成了Activity类代码框架的开发后就可以对其中传感器的监听类进行开发,首先给出监听器类的代码框架: privatefinalSensorListenermSensorLisener= newSensorListener(){//传感器监听器类 publicvoidonSensorChanged(intsensor,float[]values){…//该处省略了部分代码,将在后面进行介绍 } @Override publicvoidonAccuracyChanged(intsensor,intaccuracy){} publicbooleanisContain(intx,inty){//判断点是否在圆内 inttempx=(int)(x+mv。zhongBitmap2。getWidth()/2。0); inttempy=(int)(y+mv。zhongBitmap2。getWidth()/2。0); intox=(int)(mv。zhong1_X+mv。zhongBitmap1。getWidth()/2。0); intoy=(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)){//不在圆内returnfalse; }else{//在圆内时 returntrue; } } }; 在传感器监听类中,onSensorChanged方法用于监听传感器采样值的变化,例如手机姿态的改变等。上述代码中的is-Contain方法用于判断界面中间的气泡是否出界,若出界则返回false。 完成了代码框架的开发后,便可以对传感器的监听方法onSensorChanged进行开发了,其详细代码如下: publicvoidonSensorChanged(intsensor,float[]values){ if(sensor==SensorManager。SENSOR_ORIENTATION){ doublepitch=values[SensorManager。DATA_Y]; doubleroll=values[SensorManager。DATA_Z]; intx=0;inty=0;//临时变量,算中间水泡坐标时用 inttempX=0;inttempY=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); }elseif(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); }elseif(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 } } 在onSensorChanged方法中首先得到pitch轴以及roll轴的数值,然后根据该数值的大小调整水泡在屏幕中的位置,同时需要对水泡的坐标进行判断,使其保持在自身所在外框的范围内。 此时运行该程序,并保证测试工具Sensorsimulator与Android模拟器的连通,便会观察到如图1所示的效果,通过Sensorsimulator工具模拟手机的姿态的改变,屏幕中的水泡便随之向高处运动。 8程序发布 完成了所有代码的开发后,就可以将应用程序打包发布了。本案例中只需将Eclipse工具自动生成的apk文件拷出即可,按如下步骤操作。 (1)进行正式发布之前首先需要将代码中注释为"测试时使用"的两处代码删掉,并将注释为"真机使用"代码的注释去掉。 (2)完成代码的修改后重新构建项目。 (3)打开项目文件夹下的bin目录,其中名为SPY的apk文件便为本应用程序的安装包。 (4)将SPY。apk文件拷贝到支持传感器的Android手机中运行即可完成本应用程序的安装。 9结语 通过开发基于Android平台的传感器应用---水平仪程序,读者应该对Android程序的开发有了一定的了解,同时读者也应该了解到在Android平台下使用传感器来丰富自己软件的功能是十分方便的。[!--empirenews.page--] 另外,本案例虽然只对姿态传感器进行了应用,但相信通过对本案例的学习,读者已经有能力对其他传感器进行应用,开发出更具新意的吸引人的其他应用程序。