シャカシャカシェイクリスナー

Standard
シャカシャカシェイクリスナー

「Android端末をフンッ!って振ったら、今いる場所の住所をtwitterでつぶやく」っていう機能を実装したときのメモ。GPSから逆ジオコーディングとかjavaのtwitterライブラリ、twitter4jの使い方とかはサンプルがたくさんあるので他を参照。SensorEventListenerを使った、「端末がシェイクされた」っていうのの検知部分をメモっとくばいー
 
 
物理わからん
 
「android 加速度センサー シェイク」とかでググって出てきたのがここらへん。
http://www.grandnature.net/blog/archives/2009/02/android_2.html
http://andronavi.com/2010/03/15069
 
ハイカットとローカットとかCONVERSEしか思いつかないので、先人の知恵をほぼ流用させてもらうことにした。あと、いい歳した大人が夜な夜な携帯を振りまくって得たという謎の計算式も使っちゃう。Sensor.TYPE_ACCELEROMETER使って取得したx,y,zそれぞれの軸に対する端末の傾き具合座標のイベント発生前後の差がある一定の値を超えたら、「振られた」って判断してるってことだと思う。
 
ということで「振られた」を判断するためのクラスを↓みたいに書いた。
ShakeDeterminator.java

package com.bugcloud.android.touchme.util;

import android.hardware.SensorEvent;

public class ShakeDeterminator {

    private float[] currentOrientationValues = {0.0f, 0.0f, 0.0f};
    private float[] currentAccelerationValues = {0.0f, 0.0f, 0.0f};
    private static final int LEVEL = 2;

    public boolean isShake(SensorEvent event) {
        float shakeLevel = 30 + (8 * (float)LEVEL);
        boolean result = false;

        float values[] = event.values;
        currentOrientationValues[0] = values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
        currentOrientationValues[1] = values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);
        currentOrientationValues[2] = values[2] * 0.1f + currentOrientationValues[2] * (1.0f - 0.1f);
        currentAccelerationValues[0] = values[0] - currentOrientationValues[0];
        currentAccelerationValues[1] = values[1] - currentOrientationValues[1];
        currentAccelerationValues[2] = values[2] - currentOrientationValues[2];

        float targetValue = Math.abs(currentAccelerationValues[0]) + 
                                    Math.abs(currentAccelerationValues[1]) +
                                    Math.abs(currentAccelerationValues[2]);
        if (targetValue > shakeLevel) {
            result = true;
        }
        return result;
    }
}

 
ただコレだと、端末の傾きしか見てないので正確には「振る」を検知できない気がする。端末の傾きを変えないまま上下に動かすときとか。端末の直線的な動きしか見てないので正確には「振る」を検知できない気がする。端末の位置を変えないままブンブンやるときとか。「Androidって振るときにコツがいるんですよー」で逃げれそうな気配しかしないので今回は深追いしません。。。。。。
 
上のクラスを使ったActivityが↓な感じ
 

package com.bugcloud.android.touchme;

import java.util.List;

import com.bugcloud.android.touchme.util.ShakeDeterminator;

import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.Toast;

public class TouchMe extends Activity implements SensorEventListener {
    
    private boolean registeredSensor;
    private SensorManager sensorManager;
    
    private ShakeDeterminator shakeDeterminator;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        registeredSensor = false;
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        shakeDeterminator = new ShakeDeterminator();
    }
    
    @Override
    protected void onResume()
    {
        super.onResume();
        
        List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
        if (sensors.size() > 0) {
            Sensor sensor = sensors.get(0);
            registeredSensor = sensorManager.registerListener(this,
                                                                sensor,
                                                                SensorManager.SENSOR_DELAY_NORMAL);
        }
    }

    
    @Override
    protected void onPause()
    {
        if (registeredSensor) {
            sensorManager.unregisterListener(this);
            registeredSensor = false;
        }
        
        super.onPause();
    }

    // Show Toast Message
    private void show(String message)
    {
        Toast.makeText(
            this,
            message,
            Toast.LENGTH_SHORT
        ).show();
    }

    public void onAccuracyChanged(Sensor arg0, int arg1) {
        // TODO Auto-generated method stub
        
    }

    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            if(shakeDeterminator.isShake(event)) {
                show("加速度センサーが反応した");
            }
        }
    }
}

 
shakeDeterminator.isShake(event)の戻り値がTRUEだったときにやる処理を別スレッドでやったりするときは、クラス変数にそのスレッドが動いてるかどうかのフラグでも持っといてやんないとスレッドめっちゃ立っちゃう気がします。
ShakeDeterminator.javaのLEVELはセンサーの感度、sensorManager.registerListener()の第3引数はセンサーイベントの検知頻度。実機で試した結果、LEVEL=2で、頻度はSensorManager.SENSOR_DELAY_NORMALっていうが今回の場合はちょうどよかった。
 
 
Activity殺害事件
 
Androidは画面の縦/横でレイアウトを変えたりできるわけですが、この時にActivityは一回死んでもっかいonCreate()が走る。らしい。ので、この「振る」ときにActivityがぶっ生き返しまくる。これを回避するには画面の方向を固定してやればよかと。AndroidManifest.xmlのactivityにandroid:screenOrientation属性を追加してやる。
 
AndroidManifest.xml

< ?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.bugcloud.android.touchme"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".TouchMe"
                  android:label="@string/app_name"
                   android:screenOrientation="nosensor">
            <intent -filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent>
        </activity>
    </application>
    <uses -sdk android:minSdkVersion="4" />
</manifest> 

 
 
振ると揺れるおっぱいシミュレータをtechnohippyさんより先に作ろうと思う。

Facebook comments:

6 thoughts on “シャカシャカシェイクリスナー

  1. おかみ

    スマホ shake で検索したら迷い込みました。
    いいとしこいた大人がまいごになりました。

    おかみ

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>