Android实现简易计步器功能隔天步数清零查看历史运动纪录
作者:IT随笔 发布时间:2021-07-24 00:37:02
最近需要用到计步功能,这可难坏我了,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实现简易计步器功能隔天步数清零查看历史运动纪录网站的支持!
来源:http://blog.csdn.net/qq_21051503/article/details/73064990


猜你喜欢
- 直接上代码新建DecimalInputTextWatcher类继承TextWatcher (代码可直接复制使用) import androi
- protected bool IsChineseLetter(string input,int index){int code = 0;in
- @RequestBody出现400 Bad Request的问题今天与同事调试一个接口,发现后台使用@RequestBody老是获取不到数据
- 本文研究的主要是java fastdfs客户端使用实例的相关内容,具体实现如下。什么是FastDFS?FastDFS是用c语言编写的一款开源
- 本篇我们讲解下使用spring创建bean的几种方式,创建bean,也可以叫组件注册,就是把单例bean放到spring容器中。我们定义如下
- 前言本文主要给大家介绍了关于Spring Boot集成之异步调用Async的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细
- 一、前言二、案例需求1.编写login.html登录页面,username&password两个输入框2.使用Druid数据库连接池
- 概述状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。这个模式将状态封装成独立的类,并将动作委托到 代表当前状态的对
- 在Java中,我们可以对List集合进行如下几种方式的遍历:List<Integer> list = new ArrayList
- 普通商户分账功能分账比例:目前只有”低比例分账“小于等于30%分账,分账金额需要减去(千6)手续费.每一张订单只能分发,当前订单总额的百分之
- 前言关键字Final不仅可以用来修饰变量,而且对类及其方法的继承也有很大的影响,本文将从类与方法两个方面介绍final关键字的功能。Fina
- 什么是 terms set 查询?Terms set 查询根据匹配给定字段的精确术语的最少数量返回文档。terms set 查询与 term
- 1、redis的几种常见客户端:Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持;Redisson:实现了
- 在1.zip中增加一张新图片StorageFile jpg = await KnownFolders.PicturesLibrary.Get
- io学习框架:文件:保存数据的地方。1)常见文件对象的相关构造器和方法:当进行File file = new File(filePath);
- 我们在使用一些开源调度系统(比如:elastic-job等)的时候,对于任务的执行时间通常都是有规律性的,可能是每隔半小时执行一次,或者每天
- Win32 APIWin32 API即为Microsoft 32位平台的应用程序编程接口(Application Programming I
- 本文实例讲述了Android编程开发ScrollView中ViewPager无法正常滑动问题解决方法。分享给大家供大家参考,具体如下:这里主
- 前言:顺序表的问题及思考1. 顺序表中间/头部的插入删除,时间复杂度为O(N)2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗
- Runtime.getRuntime().exec 路径包含空格1. 现象java代码通过Runtime.getRuntime().exec