这篇文章主要讲的是Motion Sensor。

一:运动传感器总览

运动传感器主要包括加速度,重力,旋转矢量等传感器,下面是所有的android平台的运动传感器,以及SonsorEvent的values返回值。

 运动传感器可用于监控设备移动,例如倾斜,摇晃,旋转或摆动。

 

二:重力传感器(TYPE_GRAVITY)

      重力传感器可以基于硬件,也可以基于软件。   

    重力传感器的坐标系统和加速度传感器的坐标系统相同。

    单位与加速度传感器(m / s 2)使用的单位相同,坐标系与加速度传感器使用的坐标系相同。

     重力传感器提供指示重力的方向和大小的三维矢量,从开头的图片所示SensorEvent的values分别返回x,y,z方向的值。

    以下代码是获取重力传感器实例:

 private SensorManager mSensorMgr ;
 private Sensor mSensor ;
  ......  

  /**得到SensorManager对象**/
  mSensorMgr = (SensorManager) context.getSystemService(SENSOR_SERVICE);
 
 mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_GRAVITY);

   接着注册监听和解除:

 

  @Override
    protected void onResume() {
        super.onResume();
        mSensor=manager.getDefaultSensor(Sensor.TYPE_GRAVITY);
        manager.registerListener(this,mSensor, SensorManager.SENSOR_DELAY_GAME);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (manager!=null){
            manager.unregisterListener(this);
        }
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        float[] values=event.values;
        StringBuilder sb=new StringBuilder();
        sb.append("\nx轴"+values[0]);
        sb.append("\ny轴"+values[1]);
        sb.append("\nz轴"+values[2]);
        binding.tvValue.setText(sb.toString());
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

  输出下x,y,z的值

   

   注意:

  1:手机平放桌面,当x>0时,手机左翻 ,x<0,手机右翻。

  2:手机平放桌面,当y>0时,手机上翻 ,y<0,手机下翻。

  3:手机平放桌面,当z>0时,手机朝上,z<0,手机朝下。

三:加速度计传感器(TYPE_ACCELEROMETER)

        加速度计使用标准传感器坐标系,当手机自然方向平放桌面时:

       1:手机向左翻动,x>0,反之 x<0;

        2: 手机向上,也就是抬起手机头部 y>0.  抬起手机底部,y<0;

       3:   当设备坐在桌子上(而不是加速)时,加速度计读数为g = 9.81 m / s 2。类似地,当设备处于自由下落并因此以9.81m / s 2快速加速到地面时,其加速度计读数g = 0 m / s 2。

      获取sensor与别的传感器一致,类型传TYPE_ACCELEROMETER。

      这里给出个加速度计传感器的应用,模仿微信摇一摇。

       布局文件如下:

       

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#222"
        >

        <ImageView
            android:layout_width="140dp"
            android:layout_height="140dp"
            android:layout_centerInParent="true"
            android:src="@mipmap/icon_wx"
            android:id="@+id/iv_center"
            />
        
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:gravity="center"
            >
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:background="#222"
                android:id="@+id/ll_top"
                >
                <ImageView
                    android:id="@+id/iv_top"
                    android:layout_width="match_parent"
                    android:layout_height="140dp"
                    android:src="@mipmap/shake_top"
                    />
                <TextView
                    android:id="@+id/tv_line_top"
                    android:layout_width="match_parent"
                    android:layout_height="1dp"
                    android:visibility="gone"
                    android:background="#eaeaea"
                    />
            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:background="#222"
                android:id="@+id/ll_bottom"
                >
                <TextView
                    android:id="@+id/tv_line_bottom"
                    android:layout_width="match_parent"
                    android:layout_height="1dp"
                    android:visibility="gone"
                    android:background="#eaeaea"
                    />
                <ImageView
                    android:id="@+id/iv_bottom"
                    android:layout_width="match_parent"
                    android:layout_height="140dp"
                    android:src="@mipmap/shake_bottom"
                    />

            </LinearLayout>
        </LinearLayout>

    </RelativeLayout>

一共三个部分,上下两部分用于滑动,中间一部分展示的(可有可无。。)

 获取sensor对象,注册监听就不贴出来了,主要讲下onSensorChanged方法的处理,

首先获取SensorEvent.values。根据x,y,z轴的加速度判断是否执行摇一摇展开动画。

 

 if (event.sensor.getType()==Sensor.TYPE_ACCELEROMETER){
            float x=event.values[0];
            float y=event.values[1];
            float z=event.values[2];

            if ((Math.abs(x)>17||Math.abs(y)>17||Math.abs(z)>17)&&!isShake){
                isShake=true;
                Thread thread = new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        try {
                            //展示动画效果
                            mHandler.obtainMessage(START_SHAKE).sendToTarget();
                            //上下展开停留时间。
                            Thread.sleep(1500);
                            //结束摇一摇,合并上下两部分,隐藏line
                            mHandler.obtainMessage(END_SHAKE).sendToTarget();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                thread.start();
            }

        }

handler处理如下:

    private static class MyHandler extends Handler {
        private WeakReference<AccelerometerSensorActivity> mReference;
        private AccelerometerSensorActivity mActivity;
        public MyHandler(AccelerometerSensorActivity activity) {
            mReference = new WeakReference<AccelerometerSensorActivity>(activity);
            if (mReference != null) {
                mActivity = mReference.get();
            }
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case START_SHAKE:
                    mActivity.startAnimation(false);//两张图片分散开的动画
                    break;
                case END_SHAKE:
                    //整体效果结束,
                    mActivity.isShake = false;
                    // 展示上下两种图片合并的效果
                    mActivity.startAnimation(true);
                    break;
            }
        }
    }

动画部分代码如下:

 

  /**
     * 开启 摇一摇动画
     * @param isBack 是否是返回初识状态
     */
    private void startAnimation(boolean isBack) {
        binding.tvLineTop.setVisibility(View.VISIBLE);
        binding.tvLineBottom.setVisibility(View.VISIBLE);
        //动画坐标移动的位置的类型是相对自己的
        int type = Animation.RELATIVE_TO_SELF;

        float topFromY;
        float topToY;
        float bottomFromY;
        float bottomToY;
        if (isBack) {
            topFromY = -0.5f;
            topToY = 0;
            bottomFromY = 0.5f;
            bottomToY = 0;
        } else {
            topFromY = 0;
            topToY = -0.5f;
            bottomFromY = 0;
            bottomToY = 0.5f;
        }

        //上面图片的动画效果
        TranslateAnimation topAnim = new TranslateAnimation(
                type, 0, type, 0, type, topFromY, type, topToY
        );
        topAnim.setDuration(200);
        //动画终止时停留在最后一帧~不然会回到没有执行之前的状态
        topAnim.setFillAfter(true);

        //底部的动画效果
        TranslateAnimation bottomAnim = new TranslateAnimation(
                type, 0, type, 0, type, bottomFromY, type, bottomToY
        );
        bottomAnim.setDuration(200);
        bottomAnim.setFillAfter(true);


        if (isBack) {
            bottomAnim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {}
                @Override
                public void onAnimationRepeat(Animation animation) {}
                @Override
                public void onAnimationEnd(Animation animation) {
                    //当动画结束后 , 将中间两条线GONE掉, 不让其占位
                    binding.tvLineTop.setVisibility(View.GONE);
                    binding.tvLineBottom.setVisibility(View.GONE);
                }
            });
        }
        //设置动画
        binding.llTop.startAnimation(topAnim);
        binding.llBottom.startAnimation(bottomAnim);

    }

   demo地址:摇一摇代码

四:旋转矢量传感器(rotation vector sensor)

   旋转矢量表示作为角度和轴的组合的装置的取向,其中装置围绕轴(x,y或z)旋转了角度θ。

   旋转矢量类型为TYPE_ROTATION_VECTOR= 11.

  旋转矢量的三个元素表示如下:

   

  其中旋转矢量的大小等于sin(θ/ 2),并且旋转矢量的方向等于旋转轴的方向。

  下面是旋转矢量使用的坐标系

  

  • X被定义为矢量积Y x Z.它在器件的当前位置与地面相切并且指向大约东。
  • Y在设备的当前位置与地面相切,并指向地磁北极。
  • Z指向天空并垂直于地平面。

  这里利用官方给的demo展示下效果:

  

 附上地址:

 旋转矢量demo

 五:线性加速度计传感器

    线性加速度传感器提供三维向量,表示沿着每个设备轴的加速度,不包括重力,可以使用此值执行手势检测。

     传感器坐标系与加速度传感器使用的坐标系相同,测量单位(m / s 2)也是如此。

   线性加速度如下:

linear acceleration = acceleration - acceleration due to gravity

 

  一般不考虑重力加速度时,使用线性加速度计传感器。

  六:步数计数器传感器(step counter sensor)

   步数计步器类型:Sensor.TYPE_STEP_COUNTER。

  步数计数器传感器提供自激活传感器时上次重启以来用户所采取的步数。步进计数器具有更长的延迟(最多10秒),但比步数  检测器传感器更精确。

   该传感器会针对检测到的每个步伐触发一个事件,但提供的步数是自设备启动激活该传感器以来累计的总步数,在每次设备重启后会清零,所以务必需要做数据的持久化。

   该传感器返回一个float的值,100步即100.0,以此类推。该传感器也有一个时间戳成员,记录最后一个步伐的发生事件。该传感器是需要硬件支持的,并且是非常省电的,如果需要长时间获取步伐总数,就不需要解注册该传感器,注册该传感器会一直在后台运行计步。请务必在应用程序中保持注册该传感器,否则该传感器不会被激活从而不会统计总部署。

简单写个计步器的demo,地址如下:

 计步器地址

 七:步数检测器传感器 (step detector sensor)

   步数检测器类型:Sensor.TYPE_STEP_COUNTER。  获取步数检测器传感器对象。

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

该传感器在每次用户迈出一步时触发事件,延迟预计低于2秒。对于每个用户步伐,此传感器提供一个返回值为 1.0 的事件和一个指示此步伐发生时间的时间戳。当用户在行走时,会产生一个加速度上的变化,从而出触发此传感器事件的发生。

 该传感器和步数计数器传感器都依赖硬件支持,所以用之前最好检测手机是否支持。

八:陀螺仪(gyroscope)

  陀螺仪类型:TYPE_GYROSCOPE = 4。

  返回值代表陀螺仪测量设备x,y和z轴周围的旋转速率,单位radians/second:

 

 传感器的坐标系与用于加速度传感器的坐标系相同。

 下面是手机平放桌面后获取到的数值:

 

 

 

 

更多推荐