Android实现简易计步器功能隔天步数清零查看历史运动纪录

最近需要用到计步功能,这可难坏我了,iOS端倒好,有自带的计步功能,让我惊讶的是连已爬楼层都给做好了,只需要调接口便可获得数据,我有一句MMP,我很想讲。

但是抱怨归抱怨,功能还是得事先的去实现,微信运动,乐动力,都还不错,尤其是乐动力的计步功能真的非常的强大,在UI域用户与用户交互也做得非常棒,党来内需当连续运动十步后开始计步。本想着去找他们实现的算法然后拿来用,但很明显这是不可能的。后来我搜了很多资料发现,在Android4.4 Kitkat 新增的STEP DETECTOR 以及 STEP COUNTER传感器。但是!Android的这个传感器虽然可以计步,但是所记录的步数是从你开机之时开始计算,不断累加,隔天也不会清零,并且,一旦关机后,传感器记录的数据也就清空了!这就很尴尬了,不过既然直接使用传感器数据不行,那我们就自己动手,将数据按天来保存~接下来进入正题,皮皮猿,我们走起~

先来看下我们需要解决的点有:

1、步数从开机之后不断累加,关机之后便清零,步数不能隔天清零

2、不能查看历史数据

这就好办了。我们只需将当前传感器记录的步数以每天为单位存进数据库,如果更新的步数为当天的则去更新数据库!先来看下我的界面(Demo在文章最后):

第一二张图为界面效果图,数据均是从数据取出绘制在界面上,第三张图为设置前台进程时所设置的Notification样式,当然了这个可以去自定义样式,再此我就不详细解释了。

工程的目录结构如下:

其中主要的代码都在StepService.class 中了,其中注释也都非常详细,我就直接放代码了:

/** * Created by fySpring * Date : 2017/3/24 * To do : */ public class StepService extends Service implements SensorEventListener { public static final String TAG = "StepService"; //当前日期 private static String CURRENT_DATE; //当前步数 private int CURRENT_STEP; //3秒进行一次存储 private static int saveDuration = 3000; //传感器 private SensorManager sensorManager; //数据库 private StepDataDao stepDataDao; //计步传感器类型 0-counter 1-detector private static int stepSensor = -1; //广播接收 private BroadcastReceiver mInfoReceiver; //自定义简易计时器 private TimeCount timeCount; //发送消息,用来和Service之间传递步数 private Messenger messenger = new Messenger(new MessengerHandler()); //是否有当天的记录 private boolean hasRecord; //未记录之前的步数 private int hasStepCount; //下次记录之前的步数 private int previousStepCount; private Notification.Builder builder; private NotificationManager notificationManager; private Intent nfIntent; @Override public void onCreate() { super.onCreate(); initBroadcastReceiver(); new Thread(new Runnable() { public void run() { getStepDetector(); } }).start(); startTimeCount(); initTodayData(); } @Nullable @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { /** * 此处设将Service为前台,不然当APP结束以后很容易被GC给干掉,这也就是大多数音乐播放器会在状态栏设置一个 * 原理大都是相通的 */ notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //获取一个Notification构造器 builder = new Notification.Builder(this.getApplicationContext()); /** * 设置点击通知栏打开的界面,此处需要注意了,如果你的计步界面不在主界面,则需要判断app是否已经启动, * 再来确定跳转页面,这里面太多坑,(别问我为什么知道 - -) * 总之有需要的可以和我交流 */ nfIntent = new Intent(this, MainActivity.class); builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标) .setContentTitle("今日步数"+CURRENT_STEP+"步") // 设置下拉列表里的标题 .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标 .setContentText("加油,要记得勤加运动"); // 设置上下文内容 // 获取构建好的Notification Notification stepNotification = builder.build(); notificationManager.notify(110,stepNotification); // 参数一:唯一的通知标识;参数二:通知消息。 startForeground(110, stepNotification);// 开始前台服务 return START_STICKY; } /** * 自定义handler */ private class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case Constant.MSG_FROM_CLIENT: try { //这里负责将当前的步数发送出去,可以在界面或者其他地方获取,我这里是在MainActivity中获取来更新界面 Messenger messenger = msg.replyTo; Message replyMsg = Message.obtain(null, Constant.MSG_FROM_SERVER); Bundle bundle = new Bundle(); bundle.putInt("steps", CURRENT_STEP); replyMsg.setData(bundle); messenger.send(replyMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } /** * 初始化广播 */ private void initBroadcastReceiver() { final IntentFilter filter = new IntentFilter(); // 屏幕灭屏广播 filter.addAction(Intent.ACTION_SCREEN_OFF); //关机广播 filter.addAction(Intent.ACTION_SHUTDOWN); // 屏幕解锁广播 filter.addAction(Intent.ACTION_USER_PRESENT); // 当长按电源键弹出“关机”对话或者锁屏时系统会发出这个广播 // example:有时候会用到系统对话框,权限可能很高,会覆盖在锁屏界面或者“关机”对话框之上, // 所以监听这个广播,当收到时就隐藏自己的对话,如点击pad右下角部分弹出的对话框 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); //监听日期变化 filter.addAction(Intent.ACTION_DATE_CHANGED); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIME_TICK); mInfoReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { // 屏幕灭屏广播 case Intent.ACTION_SCREEN_OFF: //屏幕熄灭改为10秒一存储 saveDuration = 10000; break; //关机广播,保存好当前数据 case Intent.ACTION_SHUTDOWN: saveStepData(); break; // 屏幕解锁广播 case Intent.ACTION_USER_PRESENT: saveDuration = 3000; break; // 当长按电源键弹出“关机”对话或者锁屏时系统会发出这个广播 // example:有时候会用到系统对话框,权限可能很高,会覆盖在锁屏界面或者“关机”对话框之上, // 所以监听这个广播,当收到时就隐藏自己的对话,如点击pad右下角部分弹出的对话框 case Intent.ACTION_CLOSE_SYSTEM_DIALOGS: saveStepData(); break; //监听日期变化 case Intent.ACTION_DATE_CHANGED: case Intent.ACTION_TIME_CHANGED: case Intent.ACTION_TIME_TICK: saveStepData(); isNewDay(); break; default: break; } } }; //注册广播 registerReceiver(mInfoReceiver, filter); } /** * 初始化当天数据 */ private void initTodayData() { //获取当前时间 CURRENT_DATE = TimeUtil.getCurrentDate(); //获取数据库 stepDataDao = new StepDataDao(getApplicationContext()); //获取当天的数据,用于展示 StepEntity entity = stepDataDao.getCurDataByDate(CURRENT_DATE); //为空则说明还没有该天的数据,有则说明已经开始当天的计步了 if (entity == null) { CURRENT_STEP = 0; } else { CURRENT_STEP = Integer.parseInt(entity.getSteps()); } } /** * 监听晚上0点变化初始化数据 */ private void isNewDay() { String time = "00:00"; if (time.equals(new SimpleDateFormat("HH:mm").format(new Date())) || !CURRENT_DATE.equals(TimeUtil.getCurrentDate())) { initTodayData(); } } /** * 获取传感器实例 */ private void getStepDetector() { if (sensorManager != null) { sensorManager = null; } // 获取传感器管理器的实例 sensorManager = (SensorManager) this .getSystemService(SENSOR_SERVICE); //android4.4以后可以使用计步传感器 int VERSION_CODES = Build.VERSION.SDK_INT; if (VERSION_CODES >= 19) { addCountStepListener(); } } /** * 添加传感器监听 */ private void addCountStepListener() { Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); Sensor detectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR); if (countSensor != null) { stepSensor = 0; sensorManager.registerListener(StepService.this, countSensor, SensorManager.SENSOR_DELAY_NORMAL); } else if (detectorSensor != null) { stepSensor = 1; sensorManager.registerListener(StepService.this, detectorSensor, SensorManager.SENSOR_DELAY_NORMAL); } } /** * 由传感器记录当前用户运动步数,注意:该传感器只在4.4及以后才有,并且该传感器记录的数据是从设备开机以后不断累加, * 只有当用户关机以后,该数据才会清空,所以需要做数据保护 * * @param event */ @Override public void onSensorChanged(SensorEvent event) { if (stepSensor == 0) { int tempStep = (int) event.values[0]; if (!hasRecord) { hasRecord = true; hasStepCount = tempStep; } else { int thisStepCount = tempStep - hasStepCount; CURRENT_STEP += (thisStepCount - previousStepCount); previousStepCount = thisStepCount; } } else if (stepSensor == 1) { if (event.values[0] == 1.0) { CURRENT_STEP++; } } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } /** * 开始倒计时,去存储步数到数据库中 */ private void startTimeCount() { timeCount = new TimeCount(saveDuration, 1000); timeCount.start(); } private class TimeCount extends CountDownTimer { /** * @param millisInFuture The number of millis in the future from the call * to {@link #start()} until the countdown is done and {@link #onFinish()} * is called. * @param countDownInterval The interval along the way to receive * {@link #onTick(long)} callbacks. */ public TimeCount(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval); } @Override public void onTick(long millisUntilFinished) { } @Override public void onFinish() { // 如果计时器正常结束,则每隔三秒存储步数到数据库 timeCount.cancel(); saveStepData(); startTimeCount(); } } /** * 保存当天的数据到数据库中,并去刷新通知栏 */ private void saveStepData() { //查询数据库中的数据 StepEntity entity = stepDataDao.getCurDataByDate(CURRENT_DATE); //为空则说明还没有该天的数据,有则说明已经开始当天的计步了 if (entity == null) { //没有则新建一条数据 entity = new StepEntity(); entity.setCurDate(CURRENT_DATE); entity.setSteps(String.valueOf(CURRENT_STEP)); stepDataDao.addNewData(entity); } else { //有则更新当前的数据 entity.setSteps(String.valueOf(CURRENT_STEP)); stepDataDao.updateCurData(entity); } builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标) .setContentTitle("今日步数"+CURRENT_STEP+"步") // 设置下拉列表里的标题 .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标 .setContentText("加油,要记得勤加运动"); // 设置上下文内容  // 获取构建好的Notification Notification stepNotification = builder.build(); //调用更新 notificationManager.notify(110,stepNotification); } @Override public void onDestroy() { super.onDestroy(); //主界面中需要手动调用stop方法service才会结束 stopForeground(true); unregisterReceiver(mInfoReceiver); } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } }

其中关于四大组件之一的Service也有很多要去学习的,这几天也是恶补了一下,算是弥补当年在学校没有仔细学习这一块的遗憾吧 - -

主要要说的就是以上了,源码在这里源码点我点我

以上所述是小编给大家介绍的Android实现简易计步器功能隔天步数清零查看历史运动纪录,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

时间: 2024-07-29 01:58:47

Android实现简易计步器功能隔天步数清零查看历史运动纪录的相关文章

联想打印机多功能一体机硒鼓清零方法汇总

故障现象: 打印机或多功能一体机更换新硒鼓后,仍提示更换硒鼓或硒鼓灯常亮. 原因分析: 联想部分打印机.多功能一体机产品硒鼓计数器到期后需要进行手动清零操作,否则设备仍然会提示"硒鼓到期,请更换硒鼓"或硒鼓灯亮. 解决方案: 请您根据您设备的型号核实相应的硒鼓清零方法:   LJ2000.LJ2050N.LJ2200.LJ2250.LJ2400.LJ2600D.LJ2405D.LJ2455D.LJ2605D.LJ3000.LJ3050D.LJ3500D.LJ3550DN.LJ3600D

IOS计步器功能实现之Healthkit和CMPedometer_IOS

介绍 CMPedometer:可以访问用户活动(可以访问计步数据) 适用ios8以上的系统 ios可看最下面说明 Healthkit :是ios系统中的健康应用 需要应用授权来访问健康数据(比如计步) 这两个有什么区别呢? 其实 Healthkit 也是 使用 CMPedometer 读取用户的步数和走的里程,当然计算的方法是在手机内部计算的, 它是利用 iphone5s 以上的m处理器来获取数据计算了,所以5s以下的设备是不支持 CMPedometer 手机计步的,当然有大牛可以利用重力传感器

Android带多选功能的PhotoPicker

最近利用闲碎的一些时间开发了一个Android库PhotoPicker,前面一篇文章也介绍了,Android高仿微信图片选择功能的PhotoPicker,之前没有加入选择多张图片的功能,现在加上之后一个完整的PhotoPicker库终于算是完工了. 动态效果图 Android带多选功能的PhotoPicker示例图片 使用方法 引入PhotoPicker库,在需要使用图片选择功能的地方添加如下代码: Intent intent = new Intent(MainActivity.this, Ph

android-想实现Android的火车票查询功能,请问火车票数据从哪里获取?

问题描述 想实现Android的火车票查询功能,请问火车票数据从哪里获取? 想用Android开发火车票的查询功能,包括到达每站的时刻,所用时间,火车票价,经过的站点! 请问有现成的火车票数据?一般情况下,这些火车票数据都是从哪里获取到的? 解决方案 1.聚合网,要申请APPKEY,网址:http://www.juhe.cn/docs/api/id/22 2.webservice的,http://webservice.webxml.com.cn//WebServices/TrainTimeWeb

效果-Android Android 拖拽拼图功能的实现!!急!!在线等!急!

问题描述 Android Android 拖拽拼图功能的实现!!急!!在线等!急! 我要的效果是 比如我在左边选择了第一个模版,然后我拖拽到右边工作区域,松开放下,然后我在去左边选择模版,比如我在选择第一个模版,又拖动右边,比如我我放在我第一个的左边,这是要注意了,这2个图片不能重叠在一起!!如果我觉得第二个没放好!我可以在点击让他消失,在去左边选择一个模版,拖过来拼在上下左右的位置!!! 小弟先谢过各位了!! 有实例代码最好!!给小弟思路小弟也感激不尽!! 解决方案 在电脑中处理不行吗? 解决

调用Android自带日历功能(日历列表单、添加一个日历事件)

调用Android自带日历功能  觉得这篇文章不错,转载过来. 转载:http://blog.csdn.net/djy1992/article/details/9948393 Android手机配备有一个内置的日历应用程序.第三方应用程序可以利用日历内容提供商接口读取用户的日历信息和安排在日历新的事件.这个日历可以直接同步用户的谷歌日历.  不幸的是,没有文档和Android手机的日历应用集成,因为有另外一个联系人应用程序.相反,本文所提供的所有信息,将会通过逆向工程的谷歌日历内容提供商.该接口

源码-android 系统如何给功能打宏开关?

问题描述 android 系统如何给功能打宏开关? 比如:我做了一个通话自动录音的功能,改了源码,功能实现了,但现在问题来了,有的 客户需要这个功能,有的客户不需要这个功能,所以,想给它添加一个开关,需要这个功能的客户,将其开关打开,不需要此功能的客户,在编译系统的时候将其关闭,现在 需要了解开关是在哪个地方加,加到哪个文件里面,如何读取这个开关. 望大神指点.谢谢! 解决方案 定义全局变量Config控制, gradle打包时改变变量 解决方案二: 定义全局变量Config控制, gradle

android 集成扫码功能

问题描述 android 集成扫码功能 求可以集成到项目里的Zxing扫码工程 漂亮点的 急求! 网上找了一些不是自己想要的 解决方案 我有何老师家教通智能学习平台的邀请码cxhdd8 有谁知道怎么用吗? 解决方案二: 网上找一份还能用的自己改改就行了.

就像在windows系统下 借助汇编在exe文件中插入代码,android可不可以实现类似功能?

问题描述 就像在windows系统下 借助汇编在exe文件中插入代码,android可不可以实现类似功能? 我想开发一个app,APP中原本就有一些计算函数之类的代码,我希望该app能够实现从外部打开并导入一个包含代码的文件(比如.txt或其他),将该文件代码添加到原来的APP中 ,调用APP中原来的函数来处理该文件的代码 ,并输出结果....我不知道android可不可以实现...能的话 该怎么做呢...菜鸟一枚~还望指点