一开始,先对昨晚在昆明市火车站遇难的同胞表示默哀,并对恶*进行谴责,你们如果有再大的冤情也不要对平民下手,所谓冤有头债有主,该弄谁弄谁去啊,欺负百姓算是怎么回事,所以在遇到突发情况下,首先要有泰山崩于前而面不改色的镇定,其次要么能像成龙大哥那样以一抵十的身手,要么就是跑得快,第一项技能好像普通人是无法学会的,那我们只能学习第二项技能——跑得快,当然冰冻三尺非一日寒,这就需要大家平时多多锻炼,怎么记录提醒自己锻炼了,就和我们今天要说这个计步器APP有关了。
上次我已经和大家基本介绍了下各种传感器,今天就是传感器的实际应用,由于商业运作的原因,还暂时不能开源我做的app,文章末尾会有上传个pedometer计步器,供大家学习。
首先问下大家觉得计步器应该是用什么传感器了,其实就是利用方向传感器,根据上次说的Value的那个值进行判断,计步器原理就是人拿着手机走,走一步会产生振荡,从而方向传感获得参数Value,通过两次Value[1]的变化值之差来判断人是否走路,从而达到计步效果。核心代码如下:
public void onSensorChanged(SensorEvent event) { // 当两个values[1]值之差的绝对值大于8时认为走了一步 if (Math.abs(event.values[1] - lastPoint) > 8) { lastPoint = event.values[1]; count++; } }然后如何完整的实现这个APP了,首先我们应该构思用户界面上这个功能需要些什么,下图是我设计的主要界面:
界面看起来很复杂,其实很简单,需要一张表记录用户的记录,然后显示出来,点击开始启动service,每一分钟刷新下界面,画出一个心电图,点击结束关闭service。
首先我们来写service,第一个service从最简单刷新界面service开始,这个service很简单,当接收到广播的时候,获得相关参数进行刷新。
public class StepUpdateReceiver extends BroadcastReceiver{ public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras();//获得Bundle int steps = bundle.getInt("step");//读取步数 view1.stepsPerMin = steps+PerMin;//view1是读取了MainActivity里面的view,PerMin是记录每分钟走了几步 view1.stepsToday = steps+Today;//Today是记录今天走了几步 view1.isMoving = true;//表示用户走路 view1.updateView(); //刷新界面,重画界面 } }然后我们来写一个ManagerSerivce,用户通过点击按钮传过来广播消息,通过这个类去判断用户是进行启动还是结束,代码如下:
class ManagerReceiver extends BroadcastReceiver{ public void onReceive(Context context, Intent intent) { int result = intent.getIntExtra("result"); switch(result){ case WalkingService.CMD_STOP://停止服务 stopSelf(); break; case WalkingService.CMD_START: //SERVICE启动 isActivityOn = true; Intent i = new Intent(); i.setAction("MainActivity");i.putExtra("step", steps); sendBroadcast(i); break; } } }写完ManagerService我们开始写最关键的WalkingService:
public class WalkingService extends Service{ SensorManagerSimulator mySensorManager; WalkingListener wl; //这里自己写了个接听类 int steps=0; boolean isActivityOn = false; //Activity 是否运行 boolean isServiceOn = false; NotificationManager nm;//声明NotificationManager long timeInterval = 60*1000; final static int CMD_STOP = 0; final static int CMD_START = 1; ManagerReceiver receiver; //声明BroadcastReceiver Handler myHandler = new Handler(){//定时上传数据 public void handleMessage(Message msg) { uploadData(); super.handleMessage(msg); } }; public void onCreate() { super.onCreate(); wl = new WalkingListener(this); //创建监听器类 //初始化传感器 mySensorManager = SensorManagerSimulator.getSystemService(this, SENSOR_SERVICE); mySensorManager.connectSimulator(); //注册监听器 mySensorManager.registerListener(wl,SensorManager.SENSOR_ACCELEROMETER,SensorManager.SENSOR_DELAY_UI); nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } public void onStart(Intent intent, int startId) { super.onStart(intent, startId); isServiceOn = true; showNotification();//添加Notification receiver = new ManagerReceiver(); IntentFilter filter1 = new IntentFilter(); filter1.addAction("WalkingService"); registerReceiver(receiver, filter1); //每分钟刷新一次界面 if(isServiceOn){ Message msg =myHandler.obtainMessage(); myHandler.sendMessageDelayed(msg, timeInterval); } } public void onDestroy() { mySensorManager.unregisterListener(wl); wl = null; mySensorManager = null; nm.cancel(0); unregisterReceiver(receiver); super.onDestroy(); } private void showNotification() { Intent intent = new Intent(this,WalkingActivity.class); PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0); Notification myNotification = new Notification(); myNotification.icon = R.drawable.icon; myNotification.defaults = otification.DEFAULT_ALL; myNotification.setLatestEventInfo(this, "计步器运行中", "点击查看", pi); nm.notify(0,myNotification); } public void uploadData(){ 对数据库进行操作,并发送广播给StepUpdateSerivce } }这里主要有几个重点,第一在oncreate里面初始化传感器,和上一节的方法一致,第二在onstart注册接听器,第三在ondestory方法里面取消接听器,有些细节需要的解释的,什么是NotificatonMangager,这是一个公用类,用于在android通知栏显示app信息和手机状态信息,自己写了一个show方法用于,最小化APP的时候,用户可以在通知栏里面看到该app;为什么不时时刷新,太吃机子性能,用户体验性不好,只能改成每分钟刷新,因为需要画图。
最后我们在把核心代码写在WalkingListener里面就可以了。
public class WalkingListener implements SensorListener { WalkingService father; // WalkingService 引用 float [] preCoordinate; double currentTime=0,lastTime=0; //记录时间 float WALKING_THRESHOLD = 20; public WalkingListener(WalkingService father){ this.father = father; } public void onAccuracyChanged(int arg0, int arg1) {} //传感器发生变化后调用该方法 public void onSensorChanged(int sensor, float[] values) { if(sensor ==SensorManager.SENSOR_ACCELEROMETER){ analyseData(values);//调用方法分析数据 } } public void analyseData(float[] values){ currentTime=System.currentTimeMillis(); //每隔200MS 取加速度力和前一个进行比较 if(currentTime - lastTime >200){ if(preCoordinate == null){//还未存过数据 preCoordinate = new float[3]; for(int i=0;i<3;i++){ preCoordinate = values; }
}
else{ //进行比较 int angle= calculateAngle(values,preCoordinate); if(angle >=WALKING_THRESHOLD){ father.steps++; //步数增加 updateData(); //更新步数,并且向walkingService发送消息 } for(int i=0;i<3;i++){ preCoordinate=values; }} lastTime = currentTime;//重新计时 } }
public void updateData(){ Intent intent = new Intent(); //创建Intent 对象
intent.setAction("MainActivity");
intent.putExtra("step", father.steps);//添加步数 father.sendBroadcast(intent); //发出广播 }
public int calculateAngle(float[] newPoints,float[] oldPoints){ int angle=0; float vectorProduct=0; //向量积 float newMold=0; //新向量的模 float oldMold=0; //旧向量的模 for(int i=0;i<3;i++){ vectorProduct +=newPoints*oldPoints; newMold += newPoints*newPoints; oldMold += oldPoints*oldPoints; } newMold = (float)Math.sqrt(newMold); oldMold = (float)Math.sqrt(oldMold); //计算夹角的余弦 float cosineAngle=(float)(vectorProduct/(newMold*oldMold)); //通过余弦值求角度 float fangle = (float) Math.toDegrees(Math.acos(cosineAngle)); angle = (int)fangle; return angle; //返回向量的夹角 }关于算法,因为需要精确,所以我进行的是向量夹角的判断,当夹角大于20的时候就证明用户走了一步,其实算法多样,你可以自己选择,这也是我百度到的算法,至此一个计步器就完成了。
最后希望大家能踊跃评论,大家可以说下自己想实现什么app,我先去试试,然后做成教程来写出,不然我一个人做过的app也很少,下面是网上开源的demo,我的demo等后续再上传。