View上において、ジェスチャー操作を扱うためのクラスGestureDetectorと
スクロール処理を扱うためのクラスScrollerを組み合わせた使用例を示す。
アプリの概要は以下の通りである。
・画面をタッチして指を移動したとき、それに追従する図形を描画する。
・そこからさらに、はじくような操作をしたときに図形を慣性スクロールさせて描画する。
最終的なイメージは以下のようになる。
このサンプルのレイアウトxmlは以下のようになっている。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.scrollerlesson.FlingLessonView android:id="@+id/myView" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
このように、FlingLessonViewという独自のViewを画面全体に表示している。
それではこの独自のViewのソースを見てみよう。
package com.example.scrollerlesson; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.os.Handler; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.GestureDetector.OnGestureListener; import android.view.MotionEvent; import android.view.View; import android.widget.Scroller; public class FlingLessonView extends View { private static final int ANIMATION_INTERVAL = 10; private final Handler handler = new Handler(); private Scroller scroller; private GestureDetector gestureDetector; private float currX; private float currY; /** * @param context */ public FlingLessonView(Context context) { super(context); init(context); } /** * @param context * @param attrs */ public FlingLessonView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } /** * ビューの初期化 * @param context */ private void init(Context context){ setLongClickable(true); scroller = new Scroller(context); gestureDetector = new GestureDetector(context,new OnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if(!scroller.isFinished()) scroller.abortAnimation(); currX = e2.getX(); currY = e2.getY(); invalidate(); return true; } @Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling( (int)e2.getX(), (int)e2.getY(), (int)velocityX, (int)velocityY, 0, getWidth(), 0, getHeight()); handler.post(new Runnable() { @Override public void run() { scroller.computeScrollOffset(); currX = scroller.getCurrX(); currY = scroller.getCurrY(); invalidate(); if(!scroller.isFinished()){ handler.postDelayed(this, ANIMATION_INTERVAL); } } }); return true; } @Override public boolean onDown(MotionEvent e) { // TODO Auto-generated method stub return false; } }); } private final Paint paint = new Paint(); { paint.setStyle(Style.FILL); paint.setColor(Color.GREEN); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.BLACK); canvas.drawCircle(currX, currY, 60.f, paint); } @Override public boolean onTouchEvent(MotionEvent event) { return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event); } }
47行目からのinit()メソッドがこのViewの初期化処理となる。
まずはsetLongClickable()をtrueで呼んでいる。
これをしておかないとOnGestureListenerのonScroll()、onFling()が呼ばれないようである。
51行目でGestureDetectorのインスタンスを生成している。
そのコンストラクタ引数のひとつであるOnGestureListenerの宣言が少し長くなってしまったが、
今回の例ではonScroll()とonFling()の2つだけを使用している。
65行目のonScroll()メソッドは、View上でタッチし、そのまま指を移動したときに呼び出される。
現在の位置は2番目の引数のMotionEventから取得できるので、
Viewのインスタンス変数に保持しておき、invalidate()を呼び出してonDraw()のタイミングで
新しい座標で図形が描画されるようにしておく。
85行目のonFling()メソッドは、View上で指をはじくような動作をすると呼び出される。
ここでScrollerのfling()メソッドに渡ってきた引数をそのまま渡している。
fling()メソッドでは移動後の最小座標と最大座標を指定できるので、画面内で収まる範囲とした。
この後、定期的にScrollerのgetCurrX(),getCurrY()メソッドで得られる座標を監視すると
Scrollerが内部で計算した値を取得できる。
今回の例ではスクロールが起きている間は10msごとにその座標を監視し、画面を更新するようにしてみた。
129行目からのonDraw()メソッドでは、単に現時点での座標に青い円を描画しているだけである。
135行目のonTouch()メソッドではGestureDetectorにタッチイベントを委譲している。
このようにして、フリック操作と慣性スクロールを連動させることができた。
続きに、エミュレーターで録画した動画を掲載する。
※実機では、もう少しスムーズに描画される。
[slmplayer uri=”2012/07/20120724_device.wmv”]
ピンバック: 【Android】ScaleGestureDetectorを使う | ザワプロ!
89行目と90行目 間違ってる。。。
誤)
(int)e2.getX(),
(int)e2.getY(),
正)
currX,
currY,
でしょ。
何か動きがおかしかったので、調べてしまった・・・・・・・・