タッチイベントの実装方法|Android開発

      2018/05/22

Androidでのタッチイベントを認識させる方法です。下記のサンプルではImageViewで発生したイベントを受信しています。

サンプル

準備

ピンチイベントリスナを使用しない場合は17行目をコメントアウト

public class ImageActivity extends Activity {
    private ImageView mImageView;

    private GestureDetector mGestureDetector;
    private ScaleGestureDetector mScaleGestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_xxxx);

        mImageView = (ImageView) findViewById(R.id.xxxx_img);

        /* Touch event */
        mImageView.setOnTouchListener(mTouchEventListener);
        mGestureDetector = new GestureDetector(this, mGestureListener);
        mScaleGestureDetector = new ScaleGestureDetector(this, mScaleGestureListener);
    }

    // これらの詳細は以降の項目に記載します.
    private GestureDetector.SimpleOnGestureListener mGestureListener;
    private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleGestureListener;
    private View.OnTouchListener mTouchEventListener;
}

ダブルタップリスナ

private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onDoubleTap(MotionEvent e) {
        // ダブルタップ時の処理.
        return true;
    }
};

タッチイベントリスナ

MotionEvent.ACTION_MOVEでスワイプ動作を判定します。下記サンプルではdiffXdiffYが移動量になります。

自前でピンチイベントを処理する場合

private View.OnTouchListener mTouchEventListener = new View.OnTouchListener() {
    private PointF mLastPoint = new PointF();
    private float mPreviousR = 1.f;
    private int mLastPointerCount;

    @Override
    public boolean onTouch(View v, MotionEvent e) {
        mGestureDetector.onTouchEvent(e);

        float x;
        float y;

        if (e.getPointerCount() == 1) {
            if (mLastPointerCount == 1) {
                if (e.getAction() == MotionEvent.ACTION_MOVE) {
                    x = e.getX();
                    y = e.getY();

                    float diffX = x - mLastPoint.x;
                    float diffY = y - mLastPoint.y;

                    // スワイプ時の処理.

                    mLastPoint.set(x, y);
                } else {
                    mLastPoint.set(e.getX(), e.getY());
                }
            } else {
                mLastPoint.set(e.getX(), e.getY());
            }
        }
        if (e.getPointerCount() == 2) {
            if (mLastPointerCount == 2) {
                float r;
                if (e.getAction() == MotionEvent.ACTION_MOVE) {
                    x = (e.getX(0) + e.getX(1)) / 2.f;
                    y = (e.getY(0) + e.getY(1)) / 2.f;
                    r = (float) Math.hypot(e.getX(0) - e.getX(1), e.getY(0) - e.getY(1));

                    float factor = r / mPreviousR;
                    float diffX = x - mLastPoint.x;
                    float diffY = y - mLastPoint.y;

                    // ピンチ操作時の処理.

                    mLastPoint.set(x, y);
                    mPreviousR = r;
                } else {
                    mLastPoint.set((e.getX(0) + e.getX(1)) / 2.f, (e.getY(0) + e.getY(1)) / 2.f);
                    mPreviousR = (float) Math.hypot(e.getX(0) - e.getX(1), e.getY(0) - e.getY(1));
                }
            } else {
                mLastPoint.set((e.getX(0) + e.getX(1)) / 2.f, (e.getY(0) + e.getY(1)) / 2.f);
                mPreviousR = (float) Math.hypot(e.getX(0) - e.getX(1), e.getY(0) - e.getY(1));
            }
        }

        mLastPointerCount = e.getPointerCount();

        return true;
    }
};

factorは前回イベント発生時の2点間の距離と今回の2点間の距離の比率です。
1.0なら移動無し、1.0を超えた場合はピンチアウトになります。

ピンチイベントを処理しない場合

ピンチイベントは後述のmScaleGestureListenerに任せます。こちらの方がシンプルです。

private View.OnTouchListener mTouchEventListener = new View.OnTouchListener() {
    private PointF mLastPoint = new PointF();
    private int mLastPointerCount;

    @Override
    public boolean onTouch(View v, MotionEvent e) {
        mGestureDetector.onTouchEvent(e);
        mScaleGestureDetector.onTouchEvent(e);

        float x;
        float y;

        if (e.getPointerCount() == 1) {

            if (mLastPointerCount == 1) {
                if (e.getAction() == MotionEvent.ACTION_MOVE) {
                    x = e.getX();
                    y = e.getY();

                    float diffX = x - mLastPoint.x;
                    float diffY = y - mLastPoint.y;

                    // スワイプ時の処理.

                    mLastPoint.set(x, y);
                } else {
                    mLastPoint.set(e.getX(), e.getY());
                }
            } else {
                mLastPoint.set(e.getX(), e.getY());
            }
        }

        mLastPointerCount = e.getPointerCount();

        return true;
    }
};

ピンチイベントリスナ

private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {

    private PointF mLastPoint = new PointF();

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        mLastPoint.set(detector.getCurrentSpanX() / 2.f, detector.getCurrentSpanY() / 2.f);
        return true;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float factor = detector.getScaleFactor();
        float x = detector.getCurrentSpanX() / 2.f;
        float y = detector.getCurrentSpanY() / 2.f;
        boolean useFitScale = false;

        float diffX = x - mLastPoint.x;
        float diffY = y - mLastPoint.y;

        mLastPoint.set(x, y);

        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
    }
};

getScaleFactor()で得られる値は前回イベント発生時の2点間の距離と今回の2点間の距離の比率です。
1.0なら移動無し、1.0を超えた場合はピンチアウトになります。

イベントの通知順序

以下の様な順番でイベントが通知されますが、上記サンプルの各リスナのように戻り値でtrueを返すと、イベント消費となり以降に通知されなくなります。

  • onTouch (View)
  • onTouchEvent (Activity)
  • onDoubleTap
  • onScale

onDoubleTaponScaleGestureDetector.onTouchEvent()ScaleGestureDetector.onTouchEvent()呼び出し箇所により変わります

View.onTouch でなく Activity.onTouchEvent を使用する場合

@Override
public boolean onTouchEvent(MotionEvent e) {
    mGestureDetector.onTouchEvent(e);
    mScaleGestureDetector.onTouchEvent(e);
    // Move処理として mTouchEventListener.onTouch() 相当の処理を記述する.
    return true;
}

 - Android