端末が動いたことを確認する | Whether the SP moved or not ? | Android

スポンサーリンク

どんな時に利用するか

センサーを使ったアプリ(方位磁針や傾きを使ったゲームなど)を作る際、何もしない場合は常に値を取得するため、常に小刻みに動いてしまうという問題に直面することがあります。

今回はその問題を解決する手法の1つをご紹介します。

センサーを利用する

端末が動いているかどうかを確認する方法はセンサーを利用する方法です。端末の傾きを感知してその動きの変化から端末が動いたことを感知します。
センサーを使うにはインターフェースであるSensorEventListenerを実装します。その際は、onAccuracyChangedを必ずオーバーライドします(空でよい)。

サンプルコード

public class MainActivity extends AppCompatActivity implements SensorEventListener {
    private float[] accellFloat;
    private float[] magneticFloat;
    private double oldZengoKatamukiDouble; //後ほど用いる
    private double oldSayuuKatamukiDouble; //後ほど用いる
    private float kandoFloat;              //後ほど用いる
    private SensorManager mSensorManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    protected void onResume(){
        super.onResume();
          //センサーを設定します
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mSensorManager.registerListener(
                this,
                mSensorManager.getDefaultSensor( Sensor.TYPE_ACCELEROMETER ),
                SensorManager.SENSOR_DELAY_UI );
        mSensorManager.registerListener(
                this,
                mSensorManager.getDefaultSensor( Sensor.TYPE_MAGNETIC_FIELD ),
                SensorManager.SENSOR_DELAY_UI );
    }
    @Override
    protected void onPause(){
        super.onPause();
        mSensorManager.unregisterListener(this);
    }
    @Override
    public void onSensorChanged(SensorEvent event) {
        switch (event.sensor.getType()) {
            case Sensor.TYPE_ACCELEROMETER: //加速度センサーの設定
                accellFloat = event.values.clone();
                break;
            case Sensor.TYPE_MAGNETIC_FIELD:  //磁気センサーの設定
                magneticFloat = event.values.clone();
                break;
        }
        if (accellFloat != null && magneticFloat != null) {
            float[] inR = new float[9];
            SensorManager.getRotationMatrix(
                    inR,
                    null,
                    accellFloat,
                    magneticFloat);
            float[] outR = new float[9];
            SensorManager.remapCoordinateSystem(
                    inR,
                    SensorManager.AXIS_X, SensorManager.AXIS_Y,
                    outR);
            float[] fAttitude = new float[3];
            SensorManager.getOrientation(
                    outR,
                    fAttitude);
            Log.d("Log0","端末が動きました?"+oldZengoKatamukiDouble+" "+oldSayuuKatamukiDouble);
        }
    }
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}

加速度センサーだけではなく磁気センサーも用います

SENSOR_DELAY_UIはセンサーの取得間隔です。このほかSENSOR_DELAY_NORMAL、SENSOR_DELAY_GAME、SENSOR_DELAY_FASTESTがあります。GAME、FASTESTは短い間隔(30ミリ秒)、NORMALは200ミリ秒前後となります。一般的にはUIを用います。

このコードは実行してみるとわかりますが、端末の傾きとは関係なくSENSOR_DELAY_UIの間隔で傾きの値を取得します。

これでは端末が動いているかどうかの判断はできません。そこで、以前の傾きの数値と比較し、大きく傾きが変化した場合に、端末が動いたと判断するようなコードを用意します。

            if(oldZengoKatamukiDouble > Math.toDegrees(fAttitude[1])- 10 
            && oldZengoKatamukiDouble < Math.toDegrees(fAttitude[1])+ 10 && oldSayuuKatamukiDouble > Math.toDegrees(fAttitude[2])- 10 
            && oldSayuuKatamukiDouble < Math.toDegrees(fAttitude[2])+ 10){ 
                Log.d("Log0","端末は動いていない");
            }else{
                oldZengoKatamukiDouble = Math.toDegrees(fAttitude[1]);
                oldSayuuKatamukiDouble = Math.toDegrees(fAttitude[2]);
                Log.d("Log0","端末が動いた"+oldZengoKatamukiDouble+" "+oldSayuuKatamukiDouble);
            }

前の傾きの値と今の値との間に10度以上の差があるかどうか?で判断を行っています。

前後±10度、左右±10度の範囲内では動いていない、範囲外では動いているという判断です。

まとめのサンプルコード

public class MainActivity extends AppCompatActivity implements SensorEventListener {
    private float[] accellFloat;
    private float[] magneticFloat;
    private double oldZengoKatamukiDouble;
    private double oldSayuuKatamukiDouble;
    private float kandoFloat;
    private SensorManager mSensorManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    protected void onResume(){
        super.onResume();
        kandoFloat = 10; //この値で±の値を設定します。(感度となる)

        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mSensorManager.registerListener(
                this,
                mSensorManager.getDefaultSensor( Sensor.TYPE_ACCELEROMETER ),
                SensorManager.SENSOR_DELAY_UI );
        mSensorManager.registerListener(
                this,
                mSensorManager.getDefaultSensor( Sensor.TYPE_MAGNETIC_FIELD ),
                SensorManager.SENSOR_DELAY_UI );
    }
    @Override
    protected void onPause(){
        super.onPause();
        mSensorManager.unregisterListener(this);
    }
    @Override
    public void onSensorChanged(SensorEvent event) {
        switch (event.sensor.getType()) {
            case Sensor.TYPE_ACCELEROMETER:
                accellFloat = event.values.clone();
                break;
            case Sensor.TYPE_MAGNETIC_FIELD:
                magneticFloat = event.values.clone();
                break;
        }
        if (accellFloat != null && magneticFloat != null) {
            float[] inR = new float[9];
            SensorManager.getRotationMatrix(
                    inR,
                    null,
                    accellFloat,
                    magneticFloat);
            float[] outR = new float[9];
            SensorManager.remapCoordinateSystem(
                    inR,
                    SensorManager.AXIS_X, SensorManager.AXIS_Y,
                    outR);
            float[] fAttitude = new float[3];
            SensorManager.getOrientation(
                    outR,
                    fAttitude);
            if(oldZengoKatamukiDouble > Math.toDegrees(fAttitude[1])- kandoFloat && oldZengoKatamukiDouble < Math.toDegrees(fAttitude[1])+ kandoFloat && oldSayuuKatamukiDouble > Math.toDegrees(fAttitude[2])- kandoFloat && oldSayuuKatamukiDouble < Math.toDegrees(fAttitude[2])+ kandoFloat){
                Log.d("Log0","端末は動いていない");
            }else{
                oldZengoKatamukiDouble = Math.toDegrees(fAttitude[1]);
                oldSayuuKatamukiDouble = Math.toDegrees(fAttitude[2]);
                Log.d("Log0","端末が動いた"+oldZengoKatamukiDouble+" "+oldSayuuKatamukiDouble);
            }
        }
    }
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}