最近需要用到計步功能,這可難壞我了,iOS端倒好,有自帶的計步功能,讓我驚訝的是連已爬樓層都給做好了,只需要調接口便可獲得數(shù)據(jù),我有一句MMP,我很想講。
創(chuàng)新互聯(lián)主營常德網(wǎng)站建設的網(wǎng)絡公司,主營網(wǎng)站建設方案,成都app軟件開發(fā)公司,常德h5小程序開發(fā)搭建,常德網(wǎng)站營銷推廣歡迎常德等地區(qū)企業(yè)咨詢
但是抱怨歸抱怨,功能還是得事先的去實現(xiàn),微信運動,樂動力,都還不錯,尤其是樂動力的計步功能真的非常的強大,在UI域用戶與用戶交互也做得非常棒,黨來內需當連續(xù)運動十步后開始計步。本想著去找他們實現(xiàn)的算法然后拿來用,但很明顯這是不可能的。后來我搜了很多資料發(fā)現(xiàn),在Android4.4 Kitkat 新增的STEP DETECTOR 以及 STEP COUNTER傳感器。但是!Android的這個傳感器雖然可以計步,但是所記錄的步數(shù)是從你開機之時開始計算,不斷累加,隔天也不會清零,并且,一旦關機后,傳感器記錄的數(shù)據(jù)也就清空了!這就很尷尬了,不過既然直接使用傳感器數(shù)據(jù)不行,那我們就自己動手,將數(shù)據(jù)按天來保存~接下來進入正題,皮皮猿,我們走起~
先來看下我們需要解決的點有:
1、步數(shù)從開機之后不斷累加,關機之后便清零,步數(shù)不能隔天清零
2、不能查看歷史數(shù)據(jù)
這就好辦了。我們只需將當前傳感器記錄的步數(shù)以每天為單位存進數(shù)據(jù)庫,如果更新的步數(shù)為當天的則去更新數(shù)據(jù)庫!先來看下我的界面(Demo在文章最后):
第一二張圖為界面效果圖,數(shù)據(jù)均是從數(shù)據(jù)取出繪制在界面上,第三張圖為設置前臺進程時所設置的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; //當前步數(shù) private int CURRENT_STEP; //3秒進行一次存儲 private static int saveDuration = 3000; //傳感器 private SensorManager sensorManager; //數(shù)據(jù)庫 private StepDataDao stepDataDao; //計步傳感器類型 0-counter 1-detector private static int stepSensor = -1; //廣播接收 private BroadcastReceiver mInfoReceiver; //自定義簡易計時器 private TimeCount timeCount; //發(fā)送消息,用來和Service之間傳遞步數(shù) private Messenger messenger = new Messenger(new MessengerHandler()); //是否有當天的記錄 private boolean hasRecord; //未記錄之前的步數(shù) private int hasStepCount; //下次記錄之前的步數(shù) 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給干掉,這也就是大多數(shù)音樂播放器會在狀態(tài)欄設置一個 * 原理大都是相通的 */ notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //獲取一個Notification構造器 builder = new Notification.Builder(this.getApplicationContext()); /** * 設置點擊通知欄打開的界面,此處需要注意了,如果你的計步界面不在主界面,則需要判斷app是否已經(jīng)啟動, * 再來確定跳轉頁面,這里面太多坑,(別問我為什么知道 - -) * 總之有需要的可以和我交流 */ 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("今日步數(shù)"+CURRENT_STEP+"步") // 設置下拉列表里的標題 .setSmallIcon(R.mipmap.ic_launcher) // 設置狀態(tài)欄內的小圖標 .setContentText("加油,要記得勤加運動"); // 設置上下文內容 // 獲取構建好的Notification Notification stepNotification = builder.build(); notificationManager.notify(110,stepNotification); // 參數(shù)一:唯一的通知標識;參數(shù)二:通知消息。 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 { //這里負責將當前的步數(shù)發(fā)送出去,可以在界面或者其他地方獲取,我這里是在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); // 當長按電源鍵彈出“關機”對話或者鎖屏時系統(tǒng)會發(fā)出這個廣播 // example:有時候會用到系統(tǒng)對話框,權限可能很高,會覆蓋在鎖屏界面或者“關機”對話框之上, // 所以監(jiān)聽這個廣播,當收到時就隱藏自己的對話,如點擊pad右下角部分彈出的對話框 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); //監(jiān)聽日期變化 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; //關機廣播,保存好當前數(shù)據(jù) case Intent.ACTION_SHUTDOWN: saveStepData(); break; // 屏幕解鎖廣播 case Intent.ACTION_USER_PRESENT: saveDuration = 3000; break; // 當長按電源鍵彈出“關機”對話或者鎖屏時系統(tǒng)會發(fā)出這個廣播 // example:有時候會用到系統(tǒng)對話框,權限可能很高,會覆蓋在鎖屏界面或者“關機”對話框之上, // 所以監(jiān)聽這個廣播,當收到時就隱藏自己的對話,如點擊pad右下角部分彈出的對話框 case Intent.ACTION_CLOSE_SYSTEM_DIALOGS: saveStepData(); break; //監(jiān)聽日期變化 case Intent.ACTION_DATE_CHANGED: case Intent.ACTION_TIME_CHANGED: case Intent.ACTION_TIME_TICK: saveStepData(); isNewDay(); break; default: break; } } }; //注冊廣播 registerReceiver(mInfoReceiver, filter); } /** * 初始化當天數(shù)據(jù) */ private void initTodayData() { //獲取當前時間 CURRENT_DATE = TimeUtil.getCurrentDate(); //獲取數(shù)據(jù)庫 stepDataDao = new StepDataDao(getApplicationContext()); //獲取當天的數(shù)據(jù),用于展示 StepEntity entity = stepDataDao.getCurDataByDate(CURRENT_DATE); //為空則說明還沒有該天的數(shù)據(jù),有則說明已經(jīng)開始當天的計步了 if (entity == null) { CURRENT_STEP = 0; } else { CURRENT_STEP = Integer.parseInt(entity.getSteps()); } } /** * 監(jiān)聽晚上0點變化初始化數(shù)據(jù) */ 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(); } } /** * 添加傳感器監(jiān)聽 */ 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); } } /** * 由傳感器記錄當前用戶運動步數(shù),注意:該傳感器只在4.4及以后才有,并且該傳感器記錄的數(shù)據(jù)是從設備開機以后不斷累加, * 只有當用戶關機以后,該數(shù)據(jù)才會清空,所以需要做數(shù)據(jù)保護 * * @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) { } /** * 開始倒計時,去存儲步數(shù)到數(shù)據(jù)庫中 */ 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() { // 如果計時器正常結束,則每隔三秒存儲步數(shù)到數(shù)據(jù)庫 timeCount.cancel(); saveStepData(); startTimeCount(); } } /** * 保存當天的數(shù)據(jù)到數(shù)據(jù)庫中,并去刷新通知欄 */ private void saveStepData() { //查詢數(shù)據(jù)庫中的數(shù)據(jù) StepEntity entity = stepDataDao.getCurDataByDate(CURRENT_DATE); //為空則說明還沒有該天的數(shù)據(jù),有則說明已經(jīng)開始當天的計步了 if (entity == null) { //沒有則新建一條數(shù)據(jù) entity = new StepEntity(); entity.setCurDate(CURRENT_DATE); entity.setSteps(String.valueOf(CURRENT_STEP)); stepDataDao.addNewData(entity); } else { //有則更新當前的數(shù)據(jù) 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("今日步數(shù)"+CURRENT_STEP+"步") // 設置下拉列表里的標題 .setSmallIcon(R.mipmap.ic_launcher) // 設置狀態(tài)欄內的小圖標 .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實現(xiàn)簡易計步器功能隔天步數(shù)清零查看歷史運動紀錄,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對創(chuàng)新互聯(lián)網(wǎng)站的支持!