Android 2.2より実装された ScaleGestureDetector。
onTouchイベントをこいつへ食わせるだけでマルチタッチでのドラッグとピンチイン/アウトは簡単に実装できる。
が、従来からある GestureDetectorの機能、タッチ系やシングルタッチでのドラッグは ScaleGestureDetectorには含まれていない。
どうにか組み合わせて使えないかと調べてみたけれど、独自実装しか見あたらないので試行錯誤してみた。
今のところ以下のようなコードでほぼ期待通り*1 に動いている。
@Override public boolean onTouch(View v, MotionEvent event) { if (mScaleGestureDetector != null) { final boolean isInProgres = mScaleGestureDetector.isInProgress(); mScaleGestureDetector.onTouchEvent(event); if (isInProgres || mScaleGestureDetector.isInProgress()) { return true; } } if (mGestureDetector != null) { return mGestureDetector.onTouchEvent(event); } return false; }
要はScaleGestureDetectorがイベントを食べ始めたタイミング、食べている最中、食べ終わったタイミングではそのまま帰り、それ以外は GestureDetector行きと。
ScaleGestureDetector.onTouchが戻り値をしっかり処理してくれれば isInProgress()周りの処理が簡潔になるんだが。
2011/12/15追記
実機を入手したので調べてみた。
GestureDetector.onScrollにおいて単純に distanceX/distanceYだけをみてスクロールを実現していたのだけど、「一点目のタッチでスクロール開始&一時停止、二点目をタッチ、一点目を離す」という手順を踏んだ場合、distanceX/distanceYだけを見ていると一点目を離した時点で一点目の位置から二点目の位置へ飛んだように見える。
これは多くの場合は期待しない動作になる…と思われる。
これを回避するには onScrollに渡される MotionEventもチェックする必要がある。
具体的には以下のように同一のポインタID間の Distanceかをチェックすると。
@Override public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) { if (e1.getPointerId(0) == e2.getPointerId(0)) { // スクロール処理 return true; } return false; }
同じ事は GestureDetector.onFlingにも当てはまるので必要であればチェックするコードをいれたほうが良いかも。
2013/04/03追記
結局の所、凝った処理をするならこれらのサポートライブラリを使わずに自分で書くのが一番楽な気がする。
2013/09/01追記
画像のビューワでよくある、「フリップで複数の ImageViewを切り替えつつ、ピンチイン/アウトで拡大縮小可能、拡大時はシングルドラッグでスクロール可能、拡大された ImageViewを端までスクロールさせるとシームレスにフリップへ移行、長押しすればコンテキストメニューも表示」という案件ではカスタム ViewPagerに ScaleGestureDetectorと GestureDetectorを組み合わせたようなクラスを作成して対応した。
それなりに悩みはしたけど、結果として無理に既存の ScaleGestureDetector/GestureDetectorを使うよりは楽だったと思う。
2013/09/05追記
Android 4.xのソースにそのものずばりに近い物を見つけた : photoviewersampleを 2.xで動かしてみる
結局、泥臭くやるしかなさそうだ。
*1 マルチタッチ-シングルタッチ間の移行するタイミングで異常なドラッグ動作になる事があるのだけど、GestureDetectorがマルチタッチを処理できていないだけだと思う。二つ目以降のタッチに関するイベントを捨ててやればいける気がするけどマルチタッチ対応の実機は所有していないので今のところ放置。
GestureDetectorからコンテキストメニューを表示する
コンテキストメニューや長押しを使う場合には注意が必要。