なら@はてなブログ

福岡で働くスマートフォンエンジニア(おっさん)のブログ。更新頻度がとにかく低いのが悩み。

LinearLayoutでジェスチャーイベントを拾う

いやー、いろいろ忙しくて1ヶ月以上も放置しておりました。
相変わらずAndroidアプリ開発にどっぷりで、地元のイベントにも行けないことが多くなりました。
 
さてさて、今回の記事ですが、表題の通りです。
普通にコーディングしてできそうな気がしますが、多くのViewGroupではジェスチャーイベントが取得できないようです。
 
私は画面全体を占めるLinearLayoutでフリックイベントを検知するために、GestureDetectorを使ってみたのですがうまくいかず。
 
うまくいかないコード(SomeActivity.java

// GestureDetectorインスタンスを保持しておく
private GestureDetector mGestureDetector;
 
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    // GestureDetectorを生成(リスナーを設定する)
    mGestureDetector = new GestureDetector(this, new OnFlingListener());
    
    // LinearLayoutにonTouchListenerを設定して、そこからジェスチャーイベントを
    // 拾うようにする
    findViewById(R.id.LinearLayout01).setOnTouchListener(new OnTouchListener {
        public boolean onTouch(View v, MotionEvent event) {
            return mGestureDetector.onTouchEvent(event);
        }
    });
}
 
// GestureDetector.OnGestureListenerの実装
class OnFlingListener extends GestureDetector.SimpleOnGestureListener {
    // onFlingをオーバーライド
    onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        // do something
        Log.d("FLING_TEST", "onFling was called.");
    }
}


さて、どこが間違っているのかというと、そもそも、LinearLayoutのデフォルトの動きとして、連続したイベント(要するに、画面をタップしてから動かして離すまでの一連の動作)を取得していないようです。
実際にログを出してみるとわかりますが、タップした瞬間にイベントメソッドが呼ばれていますが、その後で指をいくら動かしても何も起こりません。
ということは、一連の動きを判定してイベントを判定するジェスチャー系の処理は、そのままでは使えないということですね。
 
で、こういうときは、とりあえず公式ドキュメントを読みます。
ViewGroup | Android Developers

ということで、こういう場合はViewGroup.onInterceptTouchEventをオーバーライドした独自Viewを作成するのがスタンダードなやり方っぽいです。
ただし、上記リンク先の注意事項にある通り、このメソッドをオーバーライドしただけではまだジェスチャーイベントは拾ってくれません。
 
詳しくは上記のドキュメントに書いてありますが、LinierLayoutでジェスチャーを取得したい場合は、まずonTouchEventメソッドがtrueを返す必要があるようです。ここでfalseを返してしまうと、以降の連続したイベント発生時にonTouchEventメソッドが呼ばれません。
それとは逆に、onInterceptTouchEventのほうでは、falseを返している限りは連続したイベントが取得できて、trueを返すとそこでイベント取得が終わってしまうようです。
 
ということで、イベント発生条件なんかを考慮しなくてよい場合のもっとも簡単な実装は、
 
onInterceptTouchEvent(MotionEvent)でfalseを返す
onTouchEvent(MotionEvent)でtrueを返す
 
ということになるでしょうか?
例によって英文読解がてきとうなので、どこか間違ってるかもしれませんが、LinearLayoutの代わりに上記の実装をしたサブクラスを使用すると、LinearLayoutでもジェスチャーを取得できました。
 
 
あー、やっぱ時間がないと文章も適当ですね。
ほんとはもうちょっときちんとソースとか説明を書きたいんですが。