Custom View: multi-touch (2) -- multi-finger collaboration

This article is a series of articles
with a picture that slides with the finger – single-touch
custom View: multi-touch (1) – finger relay
custom View: multi-touch (2) – multi-finger collaboration
custom View: Multi-touch (3) -- fighting each other

This article is a series of articles. It is recommended to read the previous articles first, otherwise you may not be able to understand the following content

The essence of the collaborative type is to find the center coordinates of all fingers, that is, the focus, and do things around the focus coordinates

Let's first post the heaviest code of the previous article, we need to modify it on the basis of the previous article

package com.example.viewtest.view

import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.example.viewtest.R

class MultiTouchView (context: Context, attrs: AttributeSet?) : View(context, attrs) {
    
    

    private val bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_choice_select)
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var offsetX = 0f
    private var offsetY = 0f
    private var downX = 0f
    private var downY = 0f
    private var originOffsetX = 0f
    private var originOffsetY = 0f

    private var trackingPointerId = 0

    override fun onDraw(canvas: Canvas?) {
    
    
        super.onDraw(canvas)
        // 绘制时候使用更新后的坐标
        canvas?.drawBitmap(bitmap, offsetX, offsetY, paint)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
    
    
        when (event?.actionMasked) {
    
    
            MotionEvent.ACTION_DOWN -> {
    
    
                trackingPointerId = event.getPointerId(0)
                // 记录手指按下的坐标
                downX = event.x
                downY = event.y

                originOffsetX = offsetX
                originOffsetY = offsetY
            }
            // 新增的手指按下会触发
            MotionEvent.ACTION_POINTER_DOWN -> {
    
    
                val actionIndex = event.actionIndex
                trackingPointerId = event.getPointerId(actionIndex)
                // 记录新手指按下的坐标
                downX = event.getX(actionIndex)
                downY = event.getY(actionIndex)

                originOffsetX = offsetX
                originOffsetY = offsetY
            }
            // 当前 view 上所剩手指大于 1 时,有手指抬起会触发
            MotionEvent.ACTION_POINTER_UP -> {
    
    
                val actionIndex = event.actionIndex
                val pointerId = event.getPointerId(actionIndex)
                // 判断抬起手指的 ID 是否是正在操作的手指 ID,如果是,则需要选一根手指来接管操作
                if (pointerId == trackingPointerId) {
    
    
                    // 需要选一根手指来接管操作,具体选哪个手指,需要我们自己写算法,这里选最后一根手指
                    // 注意,这个时候去获取当前View的所有手指的时候,是包括当前正在抬起的手指的
                    // 如果抬起的手指就是最后一根手指,那么我自己是不可能接棒我自己的
                    val newIndex = if (actionIndex == event.pointerCount - 1) {
    
    
                        event.pointerCount - 2
                    } else {
    
    
                        event.pointerCount - 1
                    }

                    trackingPointerId = event.getPointerId(newIndex)

                    downX = event.getX(newIndex)
                    downY = event.getY(newIndex)

                    originOffsetX = offsetX
                    originOffsetY = offsetY
                }

            }
            MotionEvent.ACTION_MOVE -> {
    
    
                val index = event.findPointerIndex(trackingPointerId)
                // 记录最新的手指位置
                offsetX = event.getX(index) - downX + originOffsetX
                offsetY = event.getY(index) - downY + originOffsetY
                // 触发重绘
                invalidate()
            }
        }
        return true
    }
}

How to calculate the center point of the multi-finger, that is, the focus?

For example, the center point of two fingers is the addition of the abscissa of the two points/2, and the addition of the ordinate of the two points/2

What about three o'clock? the same

the same for n points

Then we can write code

    override fun onTouchEvent(event: MotionEvent): Boolean {
    
    
        val focusX: Float
        val focusY: Float
        val pointerCount = event.pointerCount
        var sumX = 0f
        var sumY = 0f

        for (i in 0 until pointerCount) {
    
    
            sumX += event.getX(i)
            sumY += event.getY(i)
        }

        focusX = sumX / pointerCount
        focusY = sumY / pointerCount
        
        ......
        
    }

The above is the process of calculating a focus. It can be found that after the focus is calculated, the essence is that the View moves with the focus. It doesn’t matter how many fingers there are, so it becomes the original one-finger operation again.

Then we change the finger used by the one-finger logic of the rest of the code to this focus.

    override fun onTouchEvent(event: MotionEvent): Boolean {
    
    
        val focusX: Float
        val focusY: Float
        val pointerCount = event.pointerCount
        var sumX = 0f
        var sumY = 0f

        for (i in 0 until pointerCount) {
    
    
            sumX += event.getX(i)
            sumY += event.getY(i)
        }

        focusX = sumX / pointerCount
        focusY = sumY / pointerCount

        when (event.actionMasked) {
    
    
            MotionEvent.ACTION_DOWN -> {
    
    
                // 记录焦点的坐标
                downX = focusX
                downY = focusY

                originOffsetX = offsetX
                originOffsetY = offsetY
            }
            .......
            MotionEvent.ACTION_MOVE -> {
    
    
                // 记录最新的焦点位置
                offsetX = focusX - downX + originOffsetX
                offsetY = focusY - downY + originOffsetY
                // 触发重绘
                invalidate()
            }
        }
    }

The above mainly reflects that the acquisition focus is replaced by a virtual focus

So do you still need to deal with multiple fingers? If it is not processed, when a new finger is pressed, the focus position will be changed instantly, which will cause the picture to move instantaneously, so we still need to deal with multi-finger

But the processing here doesn’t need to be so complicated. No matter how many fingers are pressed or lifted, you only need to do the same thing, which is also the logic of the previous single touch: reset the focus coordinates

    override fun onTouchEvent(event: MotionEvent): Boolean {
    
    
        val focusX: Float
        val focusY: Float
        val pointerCount = event.pointerCount
        var sumX = 0f
        var sumY = 0f

        for (i in 0 until pointerCount) {
    
    
            sumX += event.getX(i)
            sumY += event.getY(i)
        }

        focusX = sumX / pointerCount
        focusY = sumY / pointerCount

        when (event.actionMasked) {
    
    
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_POINTER_UP -> {
    
    
                trackingPointerId = event.getPointerId(0)
                // 记录焦点的坐标
                downX = focusX
                downY = focusY

                originOffsetX = offsetX
                originOffsetY = offsetY
            }

            MotionEvent.ACTION_MOVE -> {
    
    
                // 记录最新的焦点位置
                offsetX = focusX - downX + originOffsetX
                offsetY = focusY - downY + originOffsetY
                // 触发重绘
                invalidate()
            }
        }
        return true
    }

We run the above code test:

  1. One finger swipe, no problem
  2. Add a finger, one moves and the other does not move (same direction), the picture moves at half speed, no problem
  3. Two fingers move together in the same direction, full speed, no problem
  4. lift a finger, broken, image jumps

I obviously dealt with the lifting of multiple fingers, why is there still a problem? In fact, the problem is in event.pointerCount . As we said before, the number of this acquisition includes the finger that is being lifted, so we say that we lift the finger When, the returned quantity does not correspond to the actual quantity

So let's say we need to handle two places

When one is ACTION_POINTER_UP , we need to return the number of fingers - 1 , and the other is to subtract this offset when calculating the offset in a loop

    override fun onTouchEvent(event: MotionEvent): Boolean {
    
    
        val focusX: Float
        val focusY: Float
        var pointerCount = event.pointerCount
        var sumX = 0f
        var sumY = 0f

        // 标记,是否是多指的手指抬起
        val isPointerUp = event.actionMasked == MotionEvent.ACTION_POINTER_UP

        for (i in 0 until pointerCount) {
    
    
            // 判断是否是抬起,并且是当前手指,当前手指的抬起不应该参与偏移计算
            val b = (isPointerUp && i == event.actionIndex)
            if (!b) {
    
    
                sumX += event.getX(i)
                sumY += event.getY(i)
            }
        }

        // 如果是多指抬起,则应该少一个数量参与计算
        if (isPointerUp) {
    
    
            pointerCount--
        }
        focusX = sumX / pointerCount
        focusY = sumY / pointerCount
    }

test

  1. One finger swipe, no problem
  2. Add a finger, one moves and the other does not move (same direction), the picture moves at half speed, no problem
  3. Two fingers move together in the same direction, full speed, no problem
  4. lift a finger, no problem

Well, we have finished explaining the type of collaboration here, let’s post all the code

package com.example.viewtest.view

import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.example.viewtest.R

class MultiTouchView2 (context: Context, attrs: AttributeSet?) : View(context, attrs) {
    
    

    private val bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_choice_select)
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var offsetX = 0f
    private var offsetY = 0f
    private var downX = 0f
    private var downY = 0f
    private var originOffsetX = 0f
    private var originOffsetY = 0f
    
    override fun onDraw(canvas: Canvas?) {
    
    
        super.onDraw(canvas)
        // 绘制时候使用更新后的坐标
        canvas?.drawBitmap(bitmap, offsetX, offsetY, paint)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
    
    
        val focusX: Float
        val focusY: Float
        var pointerCount = event.pointerCount
        var sumX = 0f
        var sumY = 0f

        // 标记,是否是多指的手指抬起
        val isPointerUp = event.actionMasked == MotionEvent.ACTION_POINTER_UP

        for (i in 0 until pointerCount) {
    
    
            // 判断是否是抬起,并且是当前手指,当前手指的抬起不应该参与偏移计算
            val b = (isPointerUp && i == event.actionIndex)
            if (!b) {
    
    
                sumX += event.getX(i)
                sumY += event.getY(i)
            }
        }

        // 如果是多指抬起,则应该少一个数量参与计算
        if (isPointerUp) {
    
    
            pointerCount--
        }
        focusX = sumX / pointerCount
        focusY = sumY / pointerCount

        when (event.actionMasked) {
    
    
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_POINTER_UP -> {
    
    
                // 记录焦点的坐标
                downX = focusX
                downY = focusY

                originOffsetX = offsetX
                originOffsetY = offsetY
            }

            MotionEvent.ACTION_MOVE -> {
    
    
                // 记录最新的焦点位置
                offsetX = focusX - downX + originOffsetX
                offsetY = focusY - downY + originOffsetY
                // 触发重绘
                invalidate()
            }
        }
        return true
    }
}

Guess you like

Origin blog.csdn.net/qq_35178391/article/details/132711706