Android 4.4后仿华为手机实现计步的效果
在市面上浏览过众多的计步软件,可惜没有开源的代码,而github上的几个开源的计步代码,要么就是记得不准,要么就是功能不完善,不稳定,于是决心自己写一个,分享给大家使用,希望大家一起来完善。
!!!:应小伙伴需求,2017年准备开始研究跑步计步功能,敬请期待,欢迎关注。
首先看一下MainActivity:
在onCreate方法中初始化Handler,onStart方法中开启服务,以备退到后台,再到前台,会触发onStart方法,以此来开启service。
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init();
}
private void init() { text_step = (TextView) findViewById(R.id.text_step); delayHandler = new Handler(this);
}
@Override
protected void onStart() { super.onStart(); setupService();
}
private void setupService() { Intent intent = new Intent(this, StepService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); startService(intent);
}
以bind形式开启service,故有ServiceConnection接收回调。
ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { try { messenger = new Messenger(service); Message msg = Message.obtain(null, Constant.MSG_FROM_CLIENT); msg.replyTo = mGetReplyMessenger; messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } }@Override public void onServiceDisconnected(ComponentName name) { }};
接收从服务端回调的步数:
private static class MessenerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case Constant.MSG_FROM_CLIENT: try { Messenger messenger = msg.replyTo; Message replyMsg = Message.obtain(null, Constant.MSG_FROM_SERVER); Bundle bundle = new Bundle(); bundle.putInt("step", StepDcretor.CURRENT_SETP); replyMsg.setData(bundle); messenger.send(replyMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } }}
接下来分析开启的StepService:
同理,在StepService中也有一个Handler,负责与MainActivity进行通讯。
private static class MessenerHandler extends Handler { @Override public void handleMessage(Message msg) {switch (msg.what) {case Constant.MSG_FROM_CLIENT: try { Messenger messenger = msg.replyTo; Message replyMsg = Message.obtain(null, Constant.MSG_FROM_SERVER); Bundle bundle = new Bundle(); bundle.putInt("step", StepDcretor.CURRENT_SETP); replyMsg.setData(bundle); messenger.send(replyMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } }}
StepService中的onCreate方法注册关屏、开屏等广播。开启一个线程,执行计步逻辑。
同时开启一个计时器,30s往数据库中写入一次数据。
@Override
public void onCreate() {super.onCreate(); initBroadcastReceiver(); new Thread(new Runnable() { public void run() {startStepDetector(); } }).start(); startTimeCount();
}
在注册的广播中,会根据用户是在前台还是后台,对存储时间也是有改变的。<code>
private void initBroadcastReceiver() {
final IntentFilter filter = new IntentFilter(); // 屏幕灭屏广播
filter.addAction(Intent.ACTION_SCREEN_OFF); //日期修改
filter.addAction(Intent.ACTION_TIME_CHANGED); //关机广播
filter.addAction(Intent.ACTION_SHUTDOWN); // 屏幕亮屏广播
filter.addAction(Intent.ACTION_SCREEN_ON); // 屏幕解锁广播
filter.addAction(Intent.ACTION_USER_PRESENT); // 当长按电源键弹出“关机”对话或者锁屏时系统会发出这个广播
// example:有时候会用到系统对话框,权限可能很高,会覆盖在锁屏界面或者“关机”对话框之上,
// 所以监听这个广播,当收到时就隐藏自己的对话,如点击pad右下角部分弹出的对话框
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);mBatInfoReceiver = new BroadcastReceiver() {@Override public void onReceive(final Context context, final Intent intent) { String action = intent.getAction(); if (Intent.ACTION_SCREEN_ON.equals(action)) { Log.v(TAG, "screen on"); } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {Log.v(TAG, "screen off"); //改为60秒一存储 duration = 60000; } else if (Intent.ACTION_USER_PRESENT.equals(action)) { Log.v(TAG, "screen unlock"); save(); //改为30秒一存储 duration = 30000; } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { Log.v(TAG, " receive Intent.ACTION_CLOSE_SYSTEM_DIALOGS"); //保存一次 save(); } else if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { Log.v(TAG, " receive ACTION_SHUTDOWN"); save(); } else if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { Log.v(TAG, " receive ACTION_TIME_CHANGED"); initTodayData(); clearStepData(); } } }; registerReceiver(mBatInfoReceiver, filter);
}
在onStartComand中,从数据库中初始化今日步数,并更新通知栏。
@Override
public int onStartCommand(Intent intent, int flags, int startId) { CURRENTDATE = getTodayDate();initTodayData(CURRENTDATE);updateNotification("今日步数:" + StepDcretor.CURRENT_SETP + " 步"); return START_STICKY;
}
同时开启Google内置计步器和加速度传感器,如若只需要其中一个,请开发者自行修改。
private void startStepDetector() {if (sensorManager != null && stepDetector != null) { sensorManager.unregisterListener(stepDetector); sensorManager = null; stepDetector = null; } sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);getLock(this); //android4.4以后可以使用计步传感器// int VERSION_CODES = android.os.Build.VERSION.SDK_INT;// if (VERSION_CODES >= 19) {// addCountStepListener();// } else {// addBasePedoListener();// } addBasePedoListener(); addCountStepListener(); }
接下来,就是比较重要的计步算法部分,StepDcretor类:
请注意这个类实现了SensorEventListener接口,在StepService中注册的就是这个类的实例。
public class StepDcretor implements SensorEventListener
接着,这个接口实现的方法onSensorChanged(SensorEvent event),会返回传感器回调的数值,传入calc_step(event)方法,等待下一步处理。
public void onSensorChanged(SensorEvent event) {Sensor sensor = event.sensor; synchronized (this) {if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) { calc_step(event); } }
}
calc_step方法算出加速度传感器的x、y、z三轴的平均数值(为了平衡在某一个方向数值过大造成的数据误差),接着交给DetectorNewStep方法处理。
synchronized private void calc_step(SensorEvent event) { average = (float) Math.sqrt(Math.pow(event.values[0], 2) + Math.pow(event.values[1], 2)
+ Math.pow(event.values[2], 2)); DetectorNewStep(average);
}
接下来,是针对波峰和波谷,进行检测,具体看注释。
/** 检测步子,并开始计步* 1.传入sersor中的数据* 2.如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步* 3.符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中* */public void DetectorNewStep(float values) {if (gravityOld == 0) {gravityOld = values;} else {if (DetectorPeak(values, gravityOld)) {timeOfLastPeak = timeOfThisPeak;timeOfNow = System.currentTimeMillis();if (timeOfNow - timeOfLastPeak >= 200&& (peakOfWave - valleyOfWave >= ThreadValue) && timeOfNow - timeOfLastPeak <= 2000) {timeOfThisPeak = timeOfNow;//更新界面的处理,不涉及到算法preStep();}if (timeOfNow - timeOfLastPeak >= 200&& (peakOfWave - valleyOfWave >= initialValue)) {timeOfThisPeak = timeOfNow;ThreadValue = Peak_Valley_Thread(peakOfWave - valleyOfWave);}}}gravityOld = values;}
往后看一下preStep方法,这个方法通过变量CountTimeState,将计步分为了三种模式,CountTimeState=0时代表还未开启计步器。
CountTimeState=1时代表预处理模式,也就是说TEMP_STEP步数如果在规定的时间内一直在增加,直到这个模式结束,那么TEMP_STEP值有效,反之,无效舍弃,目的是为了过滤点一些手机晃动带来的影响。
CountTimeState=2时代表正常计步模式
private void preStep() {if (CountTimeState == 0) {// 开启计时器time = new TimeCount(duration, 700);time.start();CountTimeState = 1;Log.v(TAG, "开启计时器");} else if (CountTimeState == 1) {TEMP_STEP++;Log.v(TAG, "计步中 TEMP_STEP:" + TEMP_STEP);} else if (CountTimeState == 2) {CURRENT_SETP++;if (onSensorChangeListener != null) {onSensorChangeListener.onChange();}}}
下面是检测波峰的方法:
/** 检测波峰* 以下四个条件判断为波峰:* 1.目前点为下降的趋势:isDirectionUp为false* 2.之前的点为上升的趋势:lastStatus为true* 3.到波峰为止,持续上升大于等于2次* 4.波峰值大于1.2g,小于2g* 记录波谷值* 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值* 2.所以要记录每次的波谷值,为了和下次的波峰做对比* */public boolean DetectorPeak(float newValue, float oldValue) {lastStatus = isDirectionUp;if (newValue >= oldValue) {isDirectionUp = true;continueUpCount++;} else {continueUpFormerCount = continueUpCount;continueUpCount = 0;isDirectionUp = false;}if (!isDirectionUp && lastStatus&& (continueUpFormerCount >= 2 && (oldValue >= 11.76 && oldValue < 19.6))) {peakOfWave = oldValue;return true;} else if (!lastStatus && isDirectionUp) {valleyOfWave = oldValue;return false;} else {return false;}}
动态生成阈值,阈值是为了跟波峰与波谷的差值进行比较,进而判断是否为1步。
/** 阈值的计算* 1.通过波峰波谷的差值计算阈值* 2.记录4个值,存入tempValue[]数组中* 3.在将数组传入函数averageValue中计算阈值* */public float Peak_Valley_Thread(float value) {float tempThread = ThreadValue;if (tempCount < valueNum) {tempValue[tempCount] = value;tempCount++;} else {tempThread = averageValue(tempValue, valueNum);for (int i = 1; i < valueNum; i++) {tempValue[i - 1] = tempValue[i];}tempValue[valueNum - 1] = value;}return tempThread;}
接着来看一下将阈值进行梯度化,取4组数值,进行梯度化,具体这些梯度化的数值怎么给出的,我可以告诉你这就是大量测试试出来的。
/** 梯度化阈值* 1.计算数组的均值* 2.通过均值将阈值梯度化在一个范围里* */public float averageValue(float value[], int n) {float ave = 0;for (int i = 0; i < n; i++) {ave += value[i];}ave = ave / valueNum;if (ave >= 8) {Log.v(TAG, "超过8");ave = (float) 4.3;} else if (ave >= 7 && ave < 8) {Log.v(TAG, "7-8");ave = (float) 3.3;} else if (ave >= 4 && ave < 7) {Log.v(TAG, "4-7");ave = (float) 2.3;} else if (ave >= 3 && ave < 4) {Log.v(TAG, "3-4");ave = (float) 2.0;} else {Log.v(TAG, "else");ave = (float) 1.7;}return ave;}
这样,就已经完成了计步的效果
源码地址:https://github.com/xfmax/BasePedo
Android 4.4后仿华为手机实现计步的效果相关推荐
- 《Android自定义控件》时钟、钟表AlarmClockView,仿华为手机世界时钟控件效果
转载请标明出处:https://blog.csdn.net/m0_38074457/article/details/85790550,本文出自[陈少华的博客] 一.效果图 二.控件结构 三.代码实现 ...
- 仿华为手机管家“一键优化”Loading加载框
仿华为手机管家"一键优化"Loading加载框 最近公司项目版本通过了没事做,闲来无聊学习下自定义view知识.偶尔看到华为手机上面的手机管家应用上面的loading图,于是想模仿 ...
- Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果
转载自:http://blog.csdn.net/guolin_blog/article/details/8689140 大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我 ...
- Android实现仿360手机卫士悬浮窗效果
大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...
- Android安装Charles证书(华为手机测试)
Android安装Charles证书(华为手机测试) 首先说明的是,最好看过其他安装证书的文章,已经会了-PC端Charles和手机代理设置-这一步骤,这样会很明白我在说什么. 方法一: 1. 通过C ...
- android studio for Mac 关于华为手机的真机连接
android studio for Mac 关于华为手机的真机连接 step one:需要确保手机的Android版本不低于应用程序所支持的最低SDK版本. step two:用数据线将手机与电脑连 ...
- android 仿华为手机悬浮窗设计
android 悬浮窗设计 最近项目中有个需求就是要在android 系统桌面上写一个悬浮球,并使其具有返回,进到主页,打开设置等功能.类似于华为手机的悬浮球.这里主要用到windowManager来 ...
- 鸿蒙恢复安卓,鸿蒙系统发布后,Android官网恢复推荐华为手机
8月15日消息,据外媒报道称,Android官网现在已经重新恢复了华为手机的推荐,在这之前,官网是没有华为(包含荣耀)手机的身影. 从目前官方推荐机型名单来看,推荐的机型包含了:Mate 20系列.M ...
- Android 10.0 SystemUI 仿华为充电动画
1.概述 在华为手机上经常看到在锁屏充电时,会出现一个充电动画,几秒钟后消失了,觉得这个动画挺好看的,所以就来自定义模仿华为动画,在10.0的产品中使用这个充电动画来增加产品亮点 如图: 2.Syst ...
最新文章
- tomcat和http简介
- 移动端怎么设计适合的表单?
- (6) ebj学习:ejb使用jpa注解
- verilog驱动ADC0809包括仿真测试
- 变压器符号_行输出变压器的结构、符号及电路分析
- Java方法中数组_Java中数组常用方法的总结
- 宝典计算机网络部分,计算机网络复习相关知识点宝典
- CodeForces - 1514B AND 0, Sum Big【快速模幂】
- Spring Boot 项目优化和Jvm调优 (楼主亲测,真实有效)
- java面试题及答案2020 阿里(八)
- 多功能表单填报系统V1.2.1-适用于在线报名系统、调查、数据收集等系统应用
- 图像处理边缘处理:Roberts算子和canny算子,对圆与矩阵进行识别
- 浙江省大学计算机一级考试试题,大学生计算机一级考试试题
- 苹果手机的计算机怎么设置快捷键大全,教程方法;苹果电脑快捷键大全最常用的都在这里了电脑技巧-琪琪词资源网...
- bzoj5394 [Ynoi2016]炸脖龙 树状数组+拓展欧拉定理
- 如何记忆和使用PNP和NPN?
- CCIE第一天---QoS
- 业务团队如何在日常工作中做稳定性?涵盖事前、事中、事后的方方面面
- python selenium自动化,Firefox自动下载文件以及浏览器相关配置
- 健身场馆应用软件提供商