Android 计步器开发

github  地址:https://github.com/zhouguangfu09/StepCounter

1.    程序图标


2.    点击图标,进入如下界面:


这个界面会有缓冲效果,然后进入程序的主界面.

3.程序主界面:

点击开始按钮,并甩动胳膊,计步器开始计数,也可以暂停计数,如下图所示:


5.每走150步,系统奖励一个星星,最高10颗星星。同时软件会粗略的计算行程、热量、速度等参数。如上图所示。


6.点击手机菜单键,点击“设置”选项,进入如下界面:


软件记步数的精准度跟用户的补偿以及体重有关,也跟用户设置的传感器的灵敏度有关系,在设置页面可以对相应的参数进行调节。一旦调节结束,可以重新开始。

7.在手机的主界面按返回键,退出程序。

 

源码部分解析

源码主要包含UI控制逻辑代码以及胳膊甩动检测步数代码,下面重点说一下 步数检测代码,主要集中在StepDector.Java文件中:

[java]  view plain  copy
  1. public void onSensorChanged(SensorEventevent) {  
  2.        // Log.i(Constant.STEP_SERVER, "StepDetector");  
  3.        Sensor sensor = event.sensor;  
  4.        // Log.i(Constant.STEP_DETECTOR,"onSensorChanged");  
  5.        synchronized (this) {  
  6.               if (sensor.getType() ==Sensor.TYPE_ORIENTATION) {  
  7.               } else {  
  8.                      int j =(sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? 1 : 0;  
  9.                      if (j == 1) {  
  10.                             float vSum =0;  
  11.                             for (int i =0; i < 3; i++) {  
  12.                                    finalfloat v = mYOffset + event.values[i] * mScale[j];  
  13.                                    vSum+= v;  
  14.                             }  
  15.                             int k = 0;  
  16.                             float v =vSum / 3;  
  17.   
  18.                             floatdirection = (v > mLastValues[k] ? 1: (v < mLastValues[k] ? -1 : 0));  
  19.                             if (direction== -mLastDirections[k]) {  
  20.                                    //Direction changed  
  21.                                    intextType = (direction > 0 ? 0 : 1); // minumum or  
  22.                                    //maximum?  
  23.                                    mLastExtremes[extType][k]= mLastValues[k];  
  24.                                    floatdiff = Math.abs(mLastExtremes[extType][k]- mLastExtremes[1 - extType][k]);  
  25.   
  26.                                    if(diff > SENSITIVITY) {  
  27.                                           booleanisAlmostAsLargeAsPrevious = diff > (mLastDiff[k] * 2 / 3);  
  28.                                           booleanisPreviousLargeEnough = mLastDiff[k] > (diff / 3);  
  29.                                           booleanisNotContra = (mLastMatch != 1 - extType);  
  30.   
  31.                                           if(isAlmostAsLargeAsPrevious && isPreviousLargeEnough &&isNotContra) {  
  32.                                                  end= System.currentTimeMillis();  
  33.                                                  if(end - start > 500) {// 此时判断为走了一步  
  34.                                                         Log.i("StepDetector","CURRENT_SETP:"  
  35.                                                                       +CURRENT_SETP);  
  36.                                                         CURRENT_SETP++;  
  37.                                                         mLastMatch= extType;  
  38.                                                         start= end;  
  39.                                                  }  
  40.                                           }else {  
  41.                                                  mLastMatch= -1;  
  42.                                           }  
  43.                                    }  
  44.                                    mLastDiff[k]= diff;  
  45.                             }  
  46.                             mLastDirections[k]= direction;  
  47.                             mLastValues[k]= v;  
  48.                      }  
  49.               }  
  50.        }  
 

这个函数是一个传感器的回调函数,在其中可以根据从系统地加速度传感器获取的数值进行胳膊甩动动作的判断。主要从以下几个方面判断:

(1)人如果走起来了,一般会连续多走几步。因此,如果没有连续4-5个波动,那么就极大可能是干扰。 

(2)人走动的波动,比坐车产生的波动要大,因此可以看波峰波谷的高度,只检测高于某个高度的波峰波谷。

(3)人的反射神经决定了人快速动的极限,怎么都不可能两步之间小于0.2秒,因此间隔小于0.2秒的波峰波谷直接跳过

通过重力加速计感应,重力变化的方向,大小。与正常走路或跑步时的重力变化比对,达到一定相似度时认为是在走路或跑步。实现起来很简单,只要手机有重力感应器就能实现。

 

1.    启动界面

程序启动界面带有缓冲效果,其中界面缓冲效果的配置文件在res/anim/目录下。如果计步器已经开始工作,则跳过程序启动界面,直接进入主界面。

[java]  view plain  copy
  1.        if (StepCounterService.FLAG || StepDetector.CURRENT_SETP > 0) {// 程序已经启动,直接跳转到运行界面  
  2.            Intent intent = new Intent(SplashActivity.this,StepCounterActivity.class); //创建一个新的Intent,指定当前应用程序上下文      
  3. //和要启动的StepActivity类  
  4.            startActivity(intent);                                         //传递这个intent给startActivity  
  5.            this.finish();  
  6.        } else {  
  7.            this.requestWindowFeature(Window.FEATURE_NO_TITLE);  
  8.            this.setContentView(R.layout.splash);  
  9.    
  10.            animation = AnimationUtils.loadAnimation(SplashActivity.this,  
  11.                   R.anim.animation_splash);  
  12.            this.findViewById(R.id.iv_index).setAnimation(animation);  
  13.            animation.setAnimationListener(new AnimationListener() {// 动画监听,通过AnimationListener可以监听Animation的运行过程  
  14.                      @Override  
  15.                      public void onAnimationStart(Animation animation){  
  16.                          // TODOAuto-generated method stub  
  17.                      }  
  18.                      @Override  
  19.                      public void onAnimationRepeat(Animationanimation) {  
  20.                          // TODOAuto-generated method stub  
  21.                      }  
  22.                      @Override  
  23.                      public void onAnimationEnd(Animation animation) {// 动画结束时跳转至运行界面  
  24.                          // TODOAuto-generated method stub  
  25.                          Intent intent = new Intent(SplashActivity.this,  
  26.                                 StepCounterActivity.class);  
  27.                          SplashActivity.this.startActivity(intent);  
  28.                          SplashActivity.this.finish();  
  29.                      }  
  30.                   });  
  31.        }  
  32. }  


2.  程序主界面

(1)界面元素的初始化(比如步数,星期,日期,运行时间,行程,卡路里,速度,开始按钮和停止按钮)。

[java]  view plain  copy
  1. //定义文本框控件  
  2.    private TextView tv_show_step;// 步数  
  3.    private TextView tv_week_day;// 星期  
  4.    private TextView tv_date;// 日期  
  5.   
  6.    private TextView tv_timer;// 运行时间  
  7.   
  8.    private TextView tv_distance;// 行程  
  9.    private TextView tv_calories;// 卡路里  
  10.    private TextView tv_velocity;// 速度  
  11.   
  12.    private Button btn_start;// 开始按钮  
  13.    private Button btn_stop;// 停止按钮  
  14.   
  15.    // 十颗星标  
  16.    private ImageView iv_star_1;                 
  17.    private ImageView iv_star_2;  
  18.    private ImageView iv_star_3;  
  19.    private ImageView iv_star_4;  
  20.    private ImageView iv_star_5;  
  21.    private ImageView iv_star_6;  
  22.    private ImageView iv_star_7;  
  23.    private ImageView iv_star_8;  
  24.    private ImageView iv_star_9;  
  25.    private ImageView iv_star_10;  
  26.   
  27.    private long timer = 0;// 运动时间  
  28.    private static long startTimer = 0;// 开始时间  
  29.    private static long tempTime = 0;  
  30.   
  31.    private Double distance = 0.0;// 路程:米  
  32.    private Double calories = 0.0;// 热量:卡路里  
  33.    private Double velocity = 0.0;// 速度:米每秒  
  34.   
  35.    private int step_length = 0;  //步长  
  36.    private int weight = 0;       //体重  
  37.    private int total_step = 0;   //走的总步数  
  38.     
  39.    DatagramSocket socket;  
  40.    InetAddress serverAddress;  
  41.    DatagramPacket  send_package1;  
  42.    private Thread thread;  //定义线程对象  


2)利用消息机制对主界面的UI元素进行更新。定义了一个handler,当检测的步数变化时,更新主界面显示的信息。主要代码如下:

[java]  view plain  copy
  1. Handler handler = new Handler() {// Handler对象用于更新当前步数,定时发送消息,调用方法查询数据用于显示??????????  
  2.                                        //主要接受子线程发送的数据, 并用此数据配合主线程更新UI  
  3.                                    //Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据,  
  4.    //Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递Message对象,(里面包含数据)  
  5.    //把这些消息放入主线程队列中,配合主线程进行更新UI。  
  6.   
  7.    @Override                  //这个方法是从父类/接口继承过来的,需要重写一次  
  8.    public void handleMessage(Message msg) {  
  9.        // TODO Auto-generated method stub  
  10.        super.handleMessage(msg);        // 此处可以更新UI  
  11.   
  12.        countDistance();     //调用距离方法,看一下走了多远  
  13.   
  14.        if (timer != 0 && distance != 0.0) {  
  15.   
  16.           // 体重、距离  
  17.            // 跑步热量(kcal)=体重(kg)×距离(公里)×1.036  
  18.           calories = weight * distance * 0.001;  
  19.           //速度velocity  
  20.           velocity = distance * 1000 / timer;  
  21.        } else {  
  22.           calories =0.0;  
  23.           velocity =0.0;  
  24.        }  
  25.   
  26.        countStep();          //调用步数方法  
  27.   
  28.        tv_show_step.setText(total_step + "");// 显示当前步数  
  29.   
  30.        tv_distance.setText(formatDouble(distance));// 显示路程  
  31.        tv_calories.setText(formatDouble(calories));// 显示卡路里  
  32.        tv_velocity.setText(formatDouble(velocity));// 显示速度  
  33.   
  34.        tv_timer.setText(getFormatTime(timer));// 显示当前运行时间  
  35.   
  36.        changeStep();// 设置当前步数和星标  
  37.   
  38.    }  

(3)初始化主界面的信息。

[java]  view plain  copy
  1. /** 
  2.  * 初始化界面 
  3.  */  
  4. private void init() {  
  5.   
  6.    step_length =SettingsActivity.sharedPreferences.getInt(  
  7.           SettingsActivity.STEP_LENGTH_VALUE, 70);  
  8.    weight =SettingsActivity.sharedPreferences.getInt(  
  9.           SettingsActivity.WEIGHT_VALUE, 50);  
  10.   
  11.    countDistance();  
  12.    countStep();  
  13.    if ((timer += tempTime) != 0 && distance != 0.0) {  //tempTime记录运动的总时间,timer记录每次运动时间  
  14.   
  15.        // 体重、距离  
  16.        // 跑步热量(kcal)=体重(kg)×距离(公里)×1.036,换算一下  
  17.        calories = weight * distance * 0.001;  
  18.   
  19.        velocity = distance * 1000 / timer;  
  20.    } else {  
  21.        calories = 0.0;  
  22.        velocity = 0.0;  
  23.    }  
  24.   
  25.    tv_timer.setText(getFormatTime(timer + tempTime));  
  26.   
  27.    tv_distance.setText(formatDouble(distance));  
  28.    tv_calories.setText(formatDouble(calories));  
  29.    tv_velocity.setText(formatDouble(velocity));  
  30.   
  31.    tv_show_step.setText(total_step + "");  
  32.   
  33.    btn_start.setEnabled(!StepCounterService.FLAG);  
  34.    btn_stop.setEnabled(StepCounterService.FLAG);  
  35.   
  36.    if(StepCounterService.FLAG) {  
  37.        btn_stop.setText(getString(R.string.pause));  
  38.    } else if (StepDetector.CURRENT_SETP > 0) {  
  39.        btn_stop.setEnabled(true);  
  40.        btn_stop.setText(getString(R.string.cancel));  
  41.    }  
  42.   
  43.    setDate();  
  44. }  

(4)计算距离和步长。

[java]  view plain  copy
  1. /** 
  2.  * 计算行走的距离                                                                        
  3.  */  
  4. private void countDistance() {  
  5.    if (StepDetector.CURRENT_SETP % 2 == 0) {  
  6.        distance = (StepDetector.CURRENT_SETP / 2) * 3 * step_length * 0.01;  
  7.    } else {  
  8.        distance = ((StepDetector.CURRENT_SETP / 2) * 3 + 1) * step_length * 0.01;  
  9.    }  
  10. }  
  11.   
  12. /** 
  13.  * 实际的步数 
  14.  */  
  15. private void countStep() {  
  16.    if (StepDetector.CURRENT_SETP % 2 == 0) {  
  17.        total_step = StepDetector.CURRENT_SETP;  
  18.    } else {  
  19.        total_step = StepDetector.CURRENT_SETP +1;  
  20.    }  
  21.     
  22.    total_step = StepDetector.CURRENT_SETP;  
  23. }  


3.    后台检测传感器信息的服务(service).

[java]  view plain  copy
  1. //service负责后台的需要长期运行的任务  
  2.  // 计步器服务  
  3.  // 运行在后台的服务程序,完成了界面部分的开发后  
  4.  // 就可以开发后台的服务类StepService  
  5.  // 注册或注销传感器监听器,在手机屏幕状态栏显示通知,与StepActivity进行通信,走过的步数记到哪里了???  
  6. public class StepCounterService extends Service {  
  7.    
  8.     public static Boolean FLAG = false;// 服务运行标志  
  9.    
  10.     private SensorManagermSensorManager;// 传感器服务  
  11.     private StepDetector detector;// 传感器监听对象  
  12.    
  13.     private PowerManager mPowerManager;// 电源管理服务  
  14.     private WakeLock mWakeLock;// 屏幕灯  
  15.    
  16.     @Override  
  17.     public IBinder onBind(Intent intent) {  
  18.        // TODOAuto-generated method stub  
  19.        return null;  
  20.     }  
  21.    
  22.     @Override  
  23.     public void onCreate() {  
  24.        // TODOAuto-generated method stub  
  25.        super.onCreate();  
  26.    
  27.        FLAG = true;// 标记为服务正在运行  
  28.    
  29.        // 创建监听器类,实例化监听对象  
  30.        detector = new StepDetector(this);  
  31.    
  32.        // 获取传感器的服务,初始化传感器  
  33.        mSensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);  
  34.        // 注册传感器,注册监听器  
  35.        mSensorManager.registerListener(detector,  
  36.               mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),  
  37.               SensorManager.SENSOR_DELAY_FASTEST);  
  38.    
  39.        // 电源管理服务  
  40.        mPowerManager = (PowerManager) this  
  41.               .getSystemService(Context.POWER_SERVICE);  
  42.        mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK  
  43.               |PowerManager.ACQUIRE_CAUSES_WAKEUP, "S");  
  44.        mWakeLock.acquire();  
  45.     }  
  46.    
  47.     @Override  
  48.     public void onDestroy() {  
  49.        // TODOAuto-generated method stub  
  50.        super.onDestroy();  
  51.        FLAG = false;// 服务停止  
  52.        if (detector != null) {  
  53.            mSensorManager.unregisterListener(detector);  
  54.        }  
  55.    
  56.        if (mWakeLock != null) {  
  57.            mWakeLock.release();  
  58.        }  
  59.     }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值