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,
でしょ。
何か動きがおかしかったので、調べてしまった・・・・・・・・