在市面上浏览过众多的计步软件,可惜没有开源的代码,而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后仿华为手机实现计步的效果相关推荐

  1. 《Android自定义控件》时钟、钟表AlarmClockView,仿华为手机世界时钟控件效果

    转载请标明出处:https://blog.csdn.net/m0_38074457/article/details/85790550,本文出自[陈少华的博客] 一.效果图 二.控件结构 三.代码实现 ...

  2. 仿华为手机管家“一键优化”Loading加载框

    仿华为手机管家"一键优化"Loading加载框 最近公司项目版本通过了没事做,闲来无聊学习下自定义view知识.偶尔看到华为手机上面的手机管家应用上面的loading图,于是想模仿 ...

  3. Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    转载自:http://blog.csdn.net/guolin_blog/article/details/8689140 大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我 ...

  4. Android实现仿360手机卫士悬浮窗效果

    大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...

  5. Android安装Charles证书(华为手机测试)

    Android安装Charles证书(华为手机测试) 首先说明的是,最好看过其他安装证书的文章,已经会了-PC端Charles和手机代理设置-这一步骤,这样会很明白我在说什么. 方法一: 1. 通过C ...

  6. android studio for Mac 关于华为手机的真机连接

    android studio for Mac 关于华为手机的真机连接 step one:需要确保手机的Android版本不低于应用程序所支持的最低SDK版本. step two:用数据线将手机与电脑连 ...

  7. android 仿华为手机悬浮窗设计

    android 悬浮窗设计 最近项目中有个需求就是要在android 系统桌面上写一个悬浮球,并使其具有返回,进到主页,打开设置等功能.类似于华为手机的悬浮球.这里主要用到windowManager来 ...

  8. 鸿蒙恢复安卓,鸿蒙系统发布后,Android官网恢复推荐华为手机

    8月15日消息,据外媒报道称,Android官网现在已经重新恢复了华为手机的推荐,在这之前,官网是没有华为(包含荣耀)手机的身影. 从目前官方推荐机型名单来看,推荐的机型包含了:Mate 20系列.M ...

  9. Android 10.0 SystemUI 仿华为充电动画

    1.概述 在华为手机上经常看到在锁屏充电时,会出现一个充电动画,几秒钟后消失了,觉得这个动画挺好看的,所以就来自定义模仿华为动画,在10.0的产品中使用这个充电动画来增加产品亮点 如图: 2.Syst ...

最新文章

  1. tomcat和http简介
  2. 移动端怎么设计适合的表单?
  3. (6) ebj学习:ejb使用jpa注解
  4. verilog驱动ADC0809包括仿真测试
  5. 变压器符号_行输出变压器的结构、符号及电路分析
  6. Java方法中数组_Java中数组常用方法的总结
  7. 宝典计算机网络部分,计算机网络复习相关知识点宝典
  8. CodeForces - 1514B AND 0, Sum Big【快速模幂】
  9. Spring Boot 项目优化和Jvm调优 (楼主亲测,真实有效)
  10. java面试题及答案2020 阿里(八)
  11. 多功能表单填报系统V1.2.1-适用于在线报名系统、调查、数据收集等系统应用
  12. 图像处理边缘处理:Roberts算子和canny算子,对圆与矩阵进行识别
  13. 浙江省大学计算机一级考试试题,大学生计算机一级考试试题
  14. 苹果手机的计算机怎么设置快捷键大全,教程方法;苹果电脑快捷键大全最常用的都在这里了电脑技巧-琪琪词资源网...
  15. bzoj5394 [Ynoi2016]炸脖龙 树状数组+拓展欧拉定理
  16. 如何记忆和使用PNP和NPN?
  17. CCIE第一天---QoS
  18. 业务团队如何在日常工作中做稳定性?涵盖事前、事中、事后的方方面面
  19. python selenium自动化,Firefox自动下载文件以及浏览器相关配置
  20. 健身场馆应用软件提供商

热门文章

  1. java 实现socks代理,包含sock4 sock5代理
  2. 工具及方法 - L型矩阵图
  3. matlab select函数,select函数
  4. 模拟QQ注册练习HTML5的表单form
  5. vue生成二维码并下载
  6. 软件测试方法名称大全
  7. div absolute 居中
  8. POJ-1873-The Fortified Forest(二进制枚举+凸包)
  9. 如何让iOS设备上App定时执行后台任务(上)
  10. 万科回应入股泰禾集团:仍存在不确定性,后者已是失信被执行人