1.相信越来越多的人开始注重健康而运动,那怎样才能更有动力呢,用数据记录下来,朋友之间相互比拼会更有东西。这也是现在计步器流行的主要原因之一!
2.原理,其实大部分的应用都是通过手机的重力传感器的监听来做的!你会发现,几乎所有计步器,只要晃动手机,都可以记步,因为晃动中就触发了重力传感器!
今天我们就来解析一下由Levente Bagi创建的计步器Podometer源码!网上也有很多其他的,但这个算是比较完整的,包含了各项配置,灵敏度,步长,甚至语音功能提醒(目前只支持部分语言:英语,法语。。,不支持中文),看完之后我相信你不仅学习到了计步器功能,还对系统喜好设置PreferenceActivity使用,和语音有所了解,也可以从中看出国外开发者是怎样的一种思维方式来开发的!
首先,主页面Pedometer
package cq.cake.pedometer;/*
* Pedometer - Android App
* Copyright (C) 2009 Levente Bagi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* 计步器主页面
*/
public class Pedometer extends AppCompatActivity {
private static final String TAG = "Pedometer";
private SharedPreferences mSettings;//共同的偏好
private PedometerSettings mPedometerSettings;//计步器设置参数
private Utils mUtils;//语音工具集
private TextView mStepValueView;
private TextView mPaceValueView;
private TextView mDistanceValueView;
private TextView mSpeedValueView;
private TextView mCaloriesValueView;
TextView mDesiredPaceView;//所需的步伐
private int mStepValue;//mStepValueView的值
private int mPaceValue;//mPaceValueView的值
private float mDistanceValue;//mDistanceValueView的值
private float mSpeedValue;//mSpeedValueView的值
private int mCaloriesValue;//mCaloriesValueView的值
private float mDesiredPaceOrSpeed;//所需的步速(步/分钟)或速度(km/h)
private int mMaintain;//是否是步行或跑步
private boolean mIsMetric;//公制和米制切换标志
private float mMaintainInc;//爬山数值
private boolean mQuitting = false; //应用是否退出
// Set when user selected Quit from menu, can be used by onPause, onStop, onDestroy
/**
* True, when service is running.
*/
private boolean mIsRunning;//程序是否运行的标志位
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "[ACTIVITY] onCreate");
super.onCreate(savedInstanceState);
mStepValue = 0;
mPaceValue = 0;
setContentView(R.layout.main);
mUtils = Utils.getInstance();
}
//开始函数,重写该函数,加入日志。
// @Override
// protected void onStart() {
// Log.i(TAG, "[ACTIVITY] onStart");
// super.onStart();
// }
//重写回复函数
@Override
protected void onResume() {
Log.i(TAG, "[ACTIVITY] onResume");
super.onResume();
//得到当前应用的喜好设置资源
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
mPedometerSettings = new PedometerSettings(mSettings);
mUtils.setSpeak(mSettings.getBoolean("speak", false));
// 读取当前服务是否正在运行
mIsRunning = mPedometerSettings.isServiceRunning();
// 如果服务没有运行,且是新的一轮记步开始,就开启服务,并绑定服务
if (!mIsRunning && mPedometerSettings.isNewStart()) {
startStepService();
bindStepService();
}
//否则就只是绑定服务
else if (mIsRunning) {
bindStepService();
}
//暂停服务,并将时间戳清0
mPedometerSettings.clearServiceRunning();
//显示步数的控件
mStepValueView = (TextView) findViewById(R.id.step_value);
mPaceValueView = (TextView) findViewById(R.id.pace_value);
mDistanceValueView = (TextView) findViewById(R.id.distance_value);
mSpeedValueView = (TextView) findViewById(R.id.speed_value);
mCaloriesValueView = (TextView) findViewById(R.id.calories_value);
mDesiredPaceView = (TextView) findViewById(R.id.desired_pace_value);
//根据偏好设置参数,是公里还是英里来显示不同的文本
mIsMetric = mPedometerSettings.isMetric();
((TextView) findViewById(R.id.distance_units)).setText(getString(
mIsMetric
? R.string.kilometers
: R.string.miles
));
((TextView) findViewById(R.id.speed_units)).setText(getString(
mIsMetric
? R.string.kilometers_per_hour
: R.string.miles_per_hour
));
//根据设置是静止,走,快走来确定是否要显示速度控制
mMaintain = mPedometerSettings.getMaintainOption();
((LinearLayout) this.findViewById(R.id.desired_pace_control)).setVisibility(
mMaintain != PedometerSettings.M_NONE ? View.VISIBLE : View.GONE
);
if (mMaintain == PedometerSettings.M_PACE) {
mMaintainInc = 5f;
mDesiredPaceOrSpeed = (float)mPedometerSettings.getDesiredPace();
}
else
if (mMaintain == PedometerSettings.M_SPEED) {
mDesiredPaceOrSpeed = mPedometerSettings.getDesiredSpeed();
mMaintainInc = 0.1f;
}
Button button1 = (Button) findViewById(R.id.button_desired_pace_lower);
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mDesiredPaceOrSpeed -= mMaintainInc;
mDesiredPaceOrSpeed = Math.round(mDesiredPaceOrSpeed * 10) / 10f;
displayDesiredPaceOrSpeed();
setDesiredPaceOrSpeed(mDesiredPaceOrSpeed);
}
});
Button button2 = (Button) findViewById(R.id.button_desired_pace_raise);
button2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mDesiredPaceOrSpeed += mMaintainInc;
mDesiredPaceOrSpeed = Math.round(mDesiredPaceOrSpeed * 10) / 10f;
displayDesiredPaceOrSpeed();
setDesiredPaceOrSpeed(mDesiredPaceOrSpeed);
}
});
//根据是步行还是跑步来显示步数还是速度
if (mMaintain != PedometerSettings.M_NONE) {
((TextView) findViewById(R.id.desired_pace_label)).setText(
mMaintain == PedometerSettings.M_PACE
? R.string.desired_pace
: R.string.desired_speed
);
}
displayDesiredPaceOrSpeed();
}
private void displayDesiredPaceOrSpeed() {
if (mMaintain == PedometerSettings.M_PACE) {
mDesiredPaceView.setText("步行速度:" + (int)mDesiredPaceOrSpeed);
}
else {
mDesiredPaceView.setText("跑步速度:" + mDesiredPaceOrSpeed);
}
}
/**
* 暂停时,也把服务解绑,并保存
*/
@Override
protected void onPause() {
Log.i(TAG, "[ACTIVITY] onPause");
if (mIsRunning) {
unbindStepService();
}
//如果退出应用就保持服务,去掉时间搓
if (mQuitting) {
mPedometerSettings.saveServiceRunningWithNullTimestamp(mIsRunning);
}
else {
mPedometerSettings.saveServiceRunningWithTimestamp(mIsRunning);
}
super.onPause();
savePaceSetting();
}
@Override
protected void onStop() {
Log.i(TAG, "[ACTIVITY] onStop");
super.onStop();
}
protected void onDestroy() {
Log.i(TAG, "[ACTIVITY] onDestroy");
super.onDestroy();
}
@SuppressLint("MissingSuperCall")
protected void onRestart() {
Log.i(TAG, "[ACTIVITY] onRestart");
super.onDestroy();
}
/**
* 设置想要的步行还是跑
* mMaintain:我的主观意思:是步行还是跑
* @param desiredPaceOrSpeed
*/
private void setDesiredPaceOrSpeed(float desiredPaceOrSpeed) {
if (mService != null) {
if (mMaintain == PedometerSettings.M_PACE) {
mService.setDesiredPace((int)desiredPaceOrSpeed);
}
else
if (mMaintain == PedometerSettings.M_SPEED) {
mService.setDesiredSpeed(desiredPaceOrSpeed);
}
}
}
private void savePaceSetting() {
mPedometerSettings.savePaceOrSpeedSetting(mMaintain, mDesiredPaceOrSpeed);
}
private StepService mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ((StepService.StepBinder)service).getService();
mService.registerCallback(mCallback);
mService.reloadSettings();
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
/**
* 开启记步服务
*/
private void startStepService() {
if (! mIsRunning) {
Log.i(TAG, "[SERVICE] Start");
mIsRunning = true;
startService(new Intent(Pedometer.this,
StepService.class));
}
}
private void bindStepService() {
Log.i(TAG, "[SERVICE] Bind");
bindService(new Intent(Pedometer.this,
StepService.class), mConnection, Context.BIND_AUTO_CREATE + Context.BIND_DEBUG_UNBIND);
}
private void unbindStepService() {
Log.i(TAG, "[SERVICE] Unbind");
unbindService(mConnection);
}
private void stopStepService() {
Log.i(TAG, "[SERVICE] Stop");
if (mService != null) {
Log.i(TAG, "[SERVICE] stopService");
stopService(new Intent(Pedometer.this,
StepService.class));
}
mIsRunning = false;
}
private void resetValues(boolean updateDisplay) {
//如果服务不为null且正在跑
if (mService != null && mIsRunning) {
mService.resetValues();
}
else {
mStepValueView.setText("0");
mPaceValueView.setText("0");
mDistanceValueView.setText("0");
mSpeedValueView.setText("0");
mCaloriesValueView.setText("0");
SharedPreferences state = getSharedPreferences("state", 0);
SharedPreferences.Editor stateEditor = state.edit();
if (updateDisplay) {
stateEditor.putInt("steps", 0);
stateEditor.putInt("pace", 0);
stateEditor.putFloat("distance", 0);
stateEditor.putFloat("speed", 0);
stateEditor.putFloat("calories", 0);
stateEditor.commit();
}
}
}
private static final int MENU_SETTINGS = 8;
private static final int MENU_QUIT = 9;
private static final int MENU_PAUSE = 1;
private static final int MENU_RESUME = 2;
private static final int MENU_RESET = 3;
/* Creates the menu items */
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
if (mIsRunning) {
menu.add(0, MENU_PAUSE, 0, R.string.pause)
.setIcon(android.R.drawable.ic_media_pause)
.setShortcut('1', 'p');
}
else {
menu.add(0, MENU_RESUME, 0, R.string.resume)
.setIcon(android.R.drawable.ic_media_play)
.setShortcut('1', 'p');
}
menu.add(0, MENU_RESET, 0, R.string.reset)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel)
.setShortcut('2', 'r');
menu.add(0, MENU_SETTINGS, 0, R.string.settings)
.setIcon(android.R.drawable.ic_menu_preferences)
.setShortcut('8', 's')
.setIntent(new Intent(this, Settings.class));
menu.add(0, MENU_QUIT, 0, R.string.quit)
.setIcon(android.R.drawable.ic_lock_power_off)
.setShortcut('9', 'q');
return true;
}
/* Handles item selections */
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_PAUSE:
unbindStepService();
stopStepService();
return true;
case MENU_RESUME:
startStepService();
bindStepService();
return true;
case MENU_RESET:
resetValues(true);
return true;
case MENU_QUIT:
resetValues(false);
unbindStepService();
stopStepService();
mQuitting = true;
finish();
return true;
}
return false;
}
// TODO: unite all into 1 type of message
private StepService.ICallback mCallback = new StepService.ICallback() {
public void stepsChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(STEPS_MSG, value, 0));
}
public void paceChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(PACE_MSG, value, 0));
}
public void distanceChanged(float value) {
mHandler.sendMessage(mHandler.obtainMessage(DISTANCE_MSG, (int)(value*1000), 0));
}
public void speedChanged(float value) {
mHandler.sendMessage(mHandler.obtainMessage(SPEED_MSG, (int)(value*1000), 0));
}
public void caloriesChanged(float value) {
mHandler.sendMessage(mHandler.obtainMessage(CALORIES_MSG, (int)(value), 0));
}
};
private static final int STEPS_MSG = 1;
private static final int PACE_MSG = 2;
private static final int DISTANCE_MSG = 3;
private static final int SPEED_MSG = 4;
private static final int CALORIES_MSG = 5;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case STEPS_MSG:
mStepValue = (int)msg.arg1;
mStepValueView.setText("" + mStepValue);
break;
case PACE_MSG:
mPaceValue = msg.arg1;
if (mPaceValue <= 0) {
mPaceValueView.setText("0");
}
else {
mPaceValueView.setText("" + (int)mPaceValue);
}
break;
case DISTANCE_MSG:
mDistanceValue = ((int)msg.arg1)/1000f;
if (mDistanceValue <= 0) {
mDistanceValueView.setText("0");
}
else {
mDistanceValueView.setText(
("" + (mDistanceValue + 0.000001f)).substring(0, 5)
);
}
break;
case SPEED_MSG:
mSpeedValue = ((int)msg.arg1)/1000f;
if (mSpeedValue <= 0) {
mSpeedValueView.setText("0");
}
else {
mSpeedValueView.setText(
("" + (mSpeedValue + 0.000001f)).substring(0, 4)
);
}
break;
case CALORIES_MSG:
mCaloriesValue = msg.arg1;
if (mCaloriesValue <= 0) {
mCaloriesValueView.setText("0");
}
else {
mCaloriesValueView.setText("" + (int)mCaloriesValue);
}
break;
default:
super.handleMessage(msg);
}
}
};
}