欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

多点触控的原理和常见多点触控场景的写法

程序员文章站 2022-03-13 21:34:03
13_多点触控的原理和常见多点触控场景的写法多点触摸MotionEvent.getActionMasked()触摸事件的结构多点触控的三种类型多点触摸MotionEvent.getActionMasked()常见值:ACTION_DOWN 第一个手指按下(之前没有任何手指触摸到View)ACTION_UP 最后一个手指抬起(抬起之后没有任何手指触摸到View,这个手指未必是ACTION_DOWN 的那个手指)ACTION_MOVE 有手指发生移动ACTION_POINTER_DOWN 额外手...

13_多点触控的原理和常见多点触控场景的写法

多点触摸

MotionEvent.getActionMasked()

常见值:

  • ACTION_DOWN 第一个手指按下(之前没有任何手指触摸到View)
  • ACTION_UP 最后一个手指抬起(抬起之后没有任何手指触摸到View,这个手指未必是ACTION_DOWN 的那个手指)
  • ACTION_MOVE 有手指发生移动
  • ACTION_POINTER_DOWN 额外手指按下(按下之前已经有别的手指触摸到View)
  • ACTION_POINTER_UP 有手指抬起,但不是最后一个(抬起之后,仍然还有别的手指在触摸着View)

触摸事件的结构

  • 触摸事件是按序列来分组的,每一组事件必然以ACTION_DOWN开头,以ACTION_UPACTION_CANCEL 结束。
  • ACTION_POINTER_DOWNACTION_POINTER_UPACTION_MOVE —样,只是事件序列中的组成部分,并不会单独分出新的事件序列
  • 触摸事件序列是针对View的,而不是针对pointer的。「某个pointer的事件」这种说法是不正确的。
  • 同一时刻,一个View要么没有事件序列,要么只有一个事件序列。

多点触控的三种类型

  • 接力型 同一时刻只有一个pointer起作用,即最新的pointer。

    典型:ListView、 RecyclerView。

    实现方式:在 ACTION_POINTER_DOWN 和 ACTION_POINTER_UP 时记录下最新的pointer,在之后的ACTION_MOVE事件中使用这个pointer来判断位置。

  • 配合型/协作型 所有触摸到View的pointer共同起作用。

    典型:ScaleGestureDetector,以及 GestureDetector 的 onScroll() 方法判断。

    实现方式:在每个DOWN、POINTER_DOWN、POINTER_UP、UP事件中使用所有pointer的坐标来共同更新焦点坐标,并在MOVE事件中使用所有pointer的坐标来判断位置。

  • 各自为战型 各个pointer做不同的事,互不影响。

    典型:支持多画笔的画板应用。

    实现方式:在每个DOWN、POINTER_DOWN事件中记录下每个pointer的 id,在MOVE事件中使用id对它们进行跟踪。

接力型例子

public class MultiTouchView1 extends View {
    private static final float IMAGE_WIDTH = Utils.dpToPixel(200);

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Bitmap bitmap;

    float downX;
    float downY;
    float offsetX;
    float offsetY;
    float originalOffsetX;
    float originalOffsetY;
    int trackingPointerId;

    public MultiTouchView1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        bitmap = Utils.getAvatar(getResources(), (int) IMAGE_WIDTH);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                trackingPointerId = event.getPointerId(0);
                downX = event.getX();
                downY = event.getY();
                originalOffsetX = offsetX;
                originalOffsetY = offsetY;
                break;
            case MotionEvent.ACTION_MOVE:
                int index = event.findPointerIndex(trackingPointerId);
                offsetX = originalOffsetX + event.getX(index) - downX;
                offsetY = originalOffsetY + event.getY(index) - downY;
                invalidate();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                int actionIndex = event.getActionIndex();
                trackingPointerId = event.getPointerId(actionIndex);
                downX = event.getX(actionIndex);
                downY = event.getY(actionIndex);
                originalOffsetX = offsetX;
                originalOffsetY = offsetY;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                actionIndex = event.getActionIndex();
                int pointerId = event.getPointerId(actionIndex);
                if (pointerId == trackingPointerId) {
                    int newIndex;
                    if (actionIndex == event.getPointerCount() - 1) {
                        newIndex = event.getPointerCount() - 2;
                    } else {
                        newIndex = event.getPointerCount() - 1;
                    }
                    trackingPointerId = event.getPointerId(newIndex);
                    downX = event.getX(actionIndex);
                    downY = event.getY(actionIndex);
                    originalOffsetX = offsetX;
                    originalOffsetY = offsetY;
                }
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(bitmap, offsetX, offsetY, paint);
    }
}

配合型例子

public class MultiTouchView2 extends View {
    private static final float IMAGE_WIDTH = Utils.dpToPixel(200);

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Bitmap bitmap;

    float downX;
    float downY;
    float offsetX;
    float offsetY;
    float originalOffsetX;
    float originalOffsetY;

    public MultiTouchView2(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        bitmap = Utils.getAvatar(getResources(), (int) IMAGE_WIDTH);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float sumX = 0;
        float sumY = 0;
        int pointerCount = event.getPointerCount();
        boolean isPointerUp = event.getActionMasked() == MotionEvent.ACTION_POINTER_UP;
        for (int i = 0; i < pointerCount; i++) {
            if (!(isPointerUp && i == event.getActionIndex())) {
                sumX += event.getX(i);
                sumY += event.getY(i);
            }
        }
        if (isPointerUp) {
            pointerCount -= 1;
        }
        float focusX = sumX / pointerCount;
        float focusY = sumY / pointerCount;
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
                downX = focusX;
                downY = focusY;
                originalOffsetX = offsetX;
                originalOffsetY = offsetY;
                break;
            case MotionEvent.ACTION_MOVE:
                offsetX = originalOffsetX + focusX - downX;
                offsetY = originalOffsetY + focusY - downY;
                invalidate();
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(bitmap, offsetX, offsetY, paint);
    }
}

各自为战型例子

public class MultiTouchView3 extends View {
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

    SparseArray<Path> paths = new SparseArray<>();

    public MultiTouchView3(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    {
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(Utils.dpToPixel(4));
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeJoin(Paint.Join.ROUND);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case ACTION_DOWN:
            case ACTION_POINTER_DOWN:
                int actionIndex = event.getActionIndex();
                int pointerId = event.getPointerId(actionIndex);
                Path path = new Path();
                path.moveTo(event.getX(actionIndex), event.getY(actionIndex));
                paths.append(pointerId, path);
                invalidate();
                break;
            case ACTION_MOVE:
                for (int i = 0; i < event.getPointerCount(); i++) {
                    pointerId = event.getPointerId(i);
                    path = paths.get(pointerId);
                    path.lineTo(event.getX(i), event.getY(i));
                }
                invalidate();
                break;
            case ACTION_UP:
            case ACTION_POINTER_UP:
                pointerId = event.getPointerId(event.getActionIndex());
                paths.remove(pointerId);
                invalidate();
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (int i = 0; i <paths.size(); i++) {
            Path path = paths.valueAt(i);
            canvas.drawPath(path, paint);
        }
    }
}

本文地址:https://blog.csdn.net/aha_jasper/article/details/110671989

相关标签: Android