android自定义View之多点触控

最近在看PhotoView的源码,里面涉及到View事件的多点触控部分知识,在看了一些资料后,先做个简单的总结。


基本知识点

  • 多点触控的事件同样是在View的onTouchEvent()方法中得到回调
  • 多点触控的基本事件类型需要通过event.getActionMasked()获取
  • 获取到的基本事件类型包括:
    • ACTION_DOWN:第一个手指初次接触到屏幕时触发
    • ACTION_POINTER_DOWN:有非主要的手指按下(即按下之前已经有手指在屏幕上
    • ACTION_UP:最后一个 手指离开屏幕时触发。
    • ACTION_POINTER_UP:有非主要的手指抬起(即抬起之后仍然有手指在屏幕上)
    • ACTION_MOVE:手指在屏幕上滑动时触发
  • 不同于单点触控,多点触摸时要获取到触摸点的坐标,需要判断触摸的是哪个点:event.getX(int pointerIndex)。这个pointerIndex就是触摸点的序号
  • 燃鹅,这个pointerIndex有点猫腻:它会在几个手指的抬起放下的过程中发生变化。GcsSloop的文章里在解释这个点的时候有点复杂化了,其实可以用一个非常简单的例子来说明:
    我们常用的ArrayList,它就是一个链表结构,手指放下(触摸屏幕)时,就相当于在list中add一个触摸点,这个点在list中的index就是pointerIndex。抬起(删除)再添加(add)pointerIndex都跟着变化。
    就是这么简单
  • 但是,如果单靠pointerIndex来记录触摸点,那在不断抬起按下的过程中,这么多变化,维护起来就太麻烦了,因此还有一个pointerId,它是一个触摸点在整个从按下到抬起过程中,唯一不变的id。它也并不是一直增长的,而是如果在中间有触摸点抬起了,那个点的id就会等待被分配给下一根手指的触摸点。
    举个栗子:
    • 依次按下0-1-2三根手指,pointerIndex和pointerId一样都依次为0-1-2,
    • 这时抬起1,pointerIndex:0-1,pointerId:0-2,
    • 再按下一根手指,pointerIndex:0-1-2,pointerId:0-2-1。
  • 由于pointerId在整个触摸过程中保持不变,所以一般用它来区分是哪根手指。
    pointerIndex可以转化为pointerId:getPointerId(int pointerIndex),
    pointerId也可以转化为pointerIndex:findPointerIndex(int pointerId)

SHOW ME THE CODE

最后以一个简单的在多点触控中追踪单个手指的栗子来直观地加深印象:

/**
 * 绘制出第二个手指第位置
 */
public class MultiTouchTest extends CustomView {
    String TAG = "Gcs";

    // 用于判断第2个手指是否存在
    boolean haveSecondPoint = false;

    // 记录第2个手指第位置
    PointF point = new PointF(0, 0);

    public MultiTouchTest(Context context) {
        this(context, null);
    }

    public MultiTouchTest(Context context, AttributeSet attrs) {
        super(context, attrs);

        mDeafultPaint.setAntiAlias(true);
        mDeafultPaint.setTextAlign(Paint.Align.CENTER);
        mDeafultPaint.setTextSize(30);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int index = event.getActionIndex();

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_POINTER_DOWN:
                // 判断是否是第2个手指按下
                if (event.getPointerId(index) == 1){
                    haveSecondPoint = true;
                    point.set(event.getY(), event.getX());
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                // 判断抬起的手指是否是第2个
                if (event.getPointerId(index) == 1){
                    haveSecondPoint = false;
                    point.set(0, 0);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (haveSecondPoint) {
                    // 通过 pointerId 来获取 pointerIndex
                    int pointerIndex = event.findPointerIndex(1);
                    // 通过 pointerIndex 来取出对应的坐标
                    point.set(event.getX(pointerIndex), event.getY(pointerIndex));
                }
                break;
        }

        invalidate();   // 刷新

        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        canvas.translate(mViewWidth/2, mViewHeight/2);
        canvas.drawText("追踪第2个按下手指的位置", 0, 0, mDeafultPaint);
        canvas.restore();

        // 如果屏幕上有第2个手指则绘制出来其位置
        if (haveSecondPoint) {
            canvas.drawCircle(point.x, point.y, 50, mDeafultPaint);
        }
    }
}

(代码来自GcsSloop的文章)


后记

以上是一个简单的总结以备知识点速查。
下一篇文章将是PhotoView的源码分析,以及仿微信朋友圈大图查看(在列表中点击小图移动到大图中心、进入时先显示缩略图、加载完成后自动扩大到大图、图片放大、点击恢复原图、向下拖拽大图缩小退出到列表小图中)

参考:
Google的官方文档
GcsSloop的相关文章

你可能感兴趣的:(android自定义View之多点触控)