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实现简易计步器功能隔天步数清零查看历史运动纪录,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android计步功能的实现代码

    本文对原文计步项目进行了精简,移除了进程服务和计时.守护进程.数据库保存等等,方便扩展功能. Android4.4以上版本,有些手机有计步传感器可以直接使用, 而有些手机没有,但有加速度传感器,也可以实现计步功能(需要计算加速度波峰波谷来判断人走一步)! 一.使用 public class MainActivity extends AppCompatActivity implements StepCallBack{ ......... @Override public void Step(int

  • Android计步模块实例代码(类似微信运动)

    最近在项目中研究计步模块,每天0点开始记录当天的步数,类似微信运动.碰到了不少坑今天有时间整理出来给大家看看. 做之前在google.baidu.github上搜了个遍没找到好的,大多数都是需要在后台存活,需要后台Service. 对于现在的各大手机厂商为了提高电池的续航里程(省电),基本上AlertManager. android.intent.action.BOOT_COMPLETED.后台Service都是被干掉的. 后台保活策略Service,基本上没什么用,被手机系统干掉只是时间问题,

  • Android实现计步进度的环形Progress

    项目中需要实现一个计步进度的环形Progress,当未达到设定目标时,绘制特定弧度((已实现步数/目标步数)*360°)的圆弧.当已实现步数大于等于目标步数时绘制整个360°圆环. 效果图: 代码实现: 设置已完成步数和目标步数: public void setStep(int stepDone, int stepGoal) { this.stepDone = stepDone; this.stepGoal = stepGoal; int progess = (stepDone * 100) /

  • 超精准的Android手机计步器开发

    亲测在小米.魅族.华为上可用,该app采用后台service计歩服务,所以只要app不被手机杀死在后台也是可以正常计歩的.上图: 1.需要在AndroidManifest.xml中添加权限 <!--计歩需要的权限--> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.WRI

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

    最近需要用到计步功能,这可难坏我了,iOS端倒好,有自带的计步功能,让我惊讶的是连已爬楼层都给做好了,只需要调接口便可获得数据,我有一句MMP,我很想讲. 但是抱怨归抱怨,功能还是得事先的去实现,微信运动,乐动力,都还不错,尤其是乐动力的计步功能真的非常的强大,在UI域用户与用户交互也做得非常棒,党来内需当连续运动十步后开始计步.本想着去找他们实现的算法然后拿来用,但很明显这是不可能的.后来我搜了很多资料发现,在Android4.4 Kitkat 新增的STEP DETECTOR 以及 STEP

  • Android实现简易计算功能

    本文实例为大家分享了Android实现简易计算功能的具体代码,供大家参考,具体内容如下 效果如图: activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_par

  • Android实现简易秒表功能

    本文实例为大家分享了Android实现秒表功能的具体代码,供大家参考,具体内容如下 今天为了给师弟们讲安卓,花了10分钟写了一个简易的秒表app,现贴出代码,供各位刚入门以及还未入门的同学们参考 第一步:布局activity_main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android

  • Android实现简易闹钟功能

    本文实例为大家分享了Android通过广播来实现闹钟的具体代码,供大家参考,具体内容如下 1.创建广播接收RepeatingAlarm.java import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class RepeatingAlarm extends BroadcastRe

  • Android实现简易计算器功能

    本项目为大家分享了Android实现计算器功能的具体代码,供大家参考,具体内容如下 项目介绍 练手项目.能实现加减乘除及括号运算. 开发思路 界面布局 1.界面布局分三大块:公式文本区.结果文本区.按钮区.  2.通过点击按钮录入数学公式,实时展示在公式文本区.  3.点击等号,计算结果展示在结果文本区.  4.另外还有清空数据和删除一个字符功能. 计算逻辑 1.将中缀表达式转换为后缀表达式  2.计算后缀表达式得出结果 其他说明 栈数据结构简单说明: 1.栈数据结构像弹夹一样,先压进去的子弹后

  • Android 超简易Zxing框架 生成二维码+扫码功能

    zxing是一个二维码的框架. 配置 1. implementation 'com.journeyapps:zxing-android-embedded:4.1.0' 如果报错在这个文件的android下加上如下配置,让其支持Java1.8,不然只有1.7.1.6 (可在'app'右键---->open module settings------>Module看到) compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 tar

  • Android实现计步器功能

    本文实例为大家分享了Android实现计步器功能的具体代码,供大家参考,具体内容如下 计步器的原理是通过手机的前后摆动模拟步伐节奏检测.我们本身在手机的传感器中就有计步器的传感器,所以这里我们直接上代码. activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/r

  • Android实现简易的闹钟功能

    本文实例为大家分享了Android实现简易的闹钟功能的具体代码,供大家参考,具体内容如下 主要是通过广播,实现一个闹钟的简易功能. 实现效果如下: 主界面为一个简易的设置闹钟Button,点击“设置闹钟”弹出时间设置窗.设置成功后,会自动弹出弹窗,提示“时间到了”. 打开Android Studio,选择File>New>New Project,选择Phone and Tablet设备下的Empty Activity,创建项目名称为“DrinkRemind”,并点击“Finish”,完成项目创

  • IOS计步器功能实现之Healthkit和CMPedometer

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

  • Android实现简易记事本

    本文实例为大家分享了Android实现简易记事本的具体代码,供大家参考,具体内容如下 此次做的Android简易记事本的存储方式使用了SQLite数据库,然后界面的实现比较简单,但是,具有增删改查的基本功能,这里可以看一下效果图,如下: 具体操作就是长按可以删除操作,点击可以进行修改,点击添加笔记按钮可以添加一个笔记. 首先我们需要三个界面样式一个是我们的进入程序时的第一个界面,然后第一个界面里面有一个ListView,这个ListView需要一个xml来描述里面的各个元素,这也是第二个.还有一

随机推荐