Android 傳感器篇:(二)運動傳感器(Motion Sensor)

這篇文章主要講的是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:

 

 傳感器的座標系與用於加速度傳感器的座標系相同。

 下面是手機平放桌面後獲取到的數值:

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章