Three types: relay type/matching type/individual type
Single touch
package com.example.myapplication.view import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.util.AttributeSet import android.view.MotionEvent import android.view.View import com.example.myapplication.dp class MultiTouchView1(context: Context, attrs: AttributeSet) : View(context, attrs) { private val bitmap = getBitmap() private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private var offsetX = 0f //Initial position private var offsetY = 0f private var downX = 0f //Pressed position private var downY = 0f private var originalOffsetX = 0f //Offset position private var originalOffsetY = 0f 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 -> { downX = event.x downY = event.y originalOffsetX = offsetX originalOffsetY = offsetY } MotionEvent.ACTION_MOVE -> { offsetX = event.x - downX + originalOffsetX offsetY = event.y - downY + originalOffsetY invalidate() } } return true } }
The touch event sequence is for the View rather than the finger
x, y, index, id belong to a point, a sequence, getX gets the finger position with index 0
public final float getX() { return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT); }
point_move two points, when the second finger is up, the index will be set to 0 instead of 1. Sometimes the 0th finger down with an index of 0 will be pressed, and then 0 will become 1
The function of index is to traverse each Point through index when a MotionEvent occurs.
getX(),getX(index),index is obtained through event.pointCount
for traverses event.point through getX(index). If a certain point is raised, an error will be reported and the Index cannot be found. In this case, you can search by id
MotionEvent.ACTION_MOVE -> { event.getX(event.actionIndex) //The ID of the finger being pressed event.getX(event.findPointerIndex(downId)) //Get by ID }
action_move is not suitable for this method, because when moving and updating in real time, there is no so-called Point, that is, index and id, which only make sense in down/up
Relay type
package com.example.myapplication.view import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.util.AttributeSet import android.view.MotionEvent import android.view.View import com.example.myapplication.dp class MultiTouchView1(context: Context, attrs: AttributeSet) : View(context, attrs) { private val bitmap =getBitmap() private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private var offsetX = 0f //Initial position private var offsetY = 0f private var downX = 0f //Pressed position private var downY = 0f private var originalOffsetX = 0f //Offset position private var originalOffsetY = 0f private var trackingPointerId = 0 //Currently pressed finger ID 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 originalOffsetX = offsetX originalOffsetY = offsetY } //Multiple MotionEvent.ACTION_POINTER_DOWN -> { val actionIndex = event.actionIndex trackingPointerId = event.getPointerId(actionIndex) //Get the ID of the pressed finger number //update takeover downX = event.getX(actionIndex) downY = event.getY(actionIndex) originalOffsetX = offsetX originalOffsetY = offsetY } MotionEvent.ACTION_POINTER_UP -> { val actionIndex = event.actionIndex val pointerId = event.getPointerId(actionIndex) if (pointerId == trackingPointerId) { //If it is the finger being tracked, replace it val newIndex = if (actionIndex == event.pointerCount - 1) { event.pointerCount - 2 } else { event.pointerCount - 1 } trackingPointerId = event.getPointerId(newIndex) //Get the ID of the pressed finger number //update takeover downX = event.getX(newIndex) downY = event.getY(newIndex) originalOffsetX = offsetX originalOffsetY = offsetY } } MotionEvent.ACTION_MOVE -> { val index = event.findPointerIndex(trackingPointerId) offsetX = event.getX(index) - downX + originalOffsetX offsetY = event.getY(index) - downY + originalOffsetY invalidate() } } return true } }
Matching type:
package com.example.myapplication.view import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.util.AttributeSet import android.view.MotionEvent import android.view.View import com.example.myapplication.dp import com.example.myapplication.getAvatar //Swipe with two fingers class MultiTouchView2(context: Context, attrs: AttributeSet) : View(context, attrs) { private val bitmap = getAvatar(resources, 200.dp.toInt()) private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private var offsetX = 0f //Initial position private var offsetY = 0f private var downX = 0f //Pressed position private var downY = 0f private var originalOffsetX = 0f //Offset position private var originalOffsetY = 0f override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawBitmap(bitmap, offsetX, offsetY, paint) } override fun onTouchEvent(event: MotionEvent): Boolean { val focusX: Float//Add the two focus values /2 val focusY : Float var pointerCount = event.pointerCount var sumX = 0f var sumY = 0f val inPointerUp = event.actionMasked == MotionEvent.ACTION_POINTER_UP //If it is lifted for (i in 0 until pointerCount){ if (!(inPointerUp & amp; & amp; i == event.actionIndex)){ //The current position is not lifted, then calculate sumX + = event.getX(i) //Get the coordinates of each point sumY + = event.getY(i) } } if (inPointerUp){ pointerCount -- //Handle additional offsets } focusX = sumX / pointerCount //Get the focus value. When lifted, the count will change focusY = sumY /pointerCount when (event.actionMasked) { MotionEvent.ACTION_DOWN,MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_POINTER_UP -> { downX = focusX downY = focusY originalOffsetX = offsetX originalOffsetY = offsetY } MotionEvent.ACTION_MOVE -> { offsetX = focusX - downX + originalOffsetX offsetY = focusY - downY + originalOffsetY invalidate() } } return true } }
Polydactyly
package com.example.myapplication.view import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.Path import android.util.AttributeSet import android.util.SparseArray import android.view.MotionEvent import android.view.View import androidx.core.util.isEmpty import com.example.myapplication.dp class MultiTouchView3(context: Context, attrs: AttributeSet) : View(context, attrs) { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private var paths = SparseArray<Path>() init { paint.style = Paint.Style.STROKE paint.strokeWidth = 4.dp paint.strokeCap = Paint.Cap.ROUND paint.strokeJoin = Paint.Join.ROUND } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) for (i in 0 until paths.size()){ val path = paths.valueAt(i) canvas.drawPath(path,paint) } } override fun onTouchEvent(event: MotionEvent): Boolean { when(event.actionMasked){ MotionEvent.ACTION_DOWN,MotionEvent.ACTION_POINTER_DOWN ->{ val actionIndex = event.actionIndex val path = Path() path.moveTo(event.getX(actionIndex),event.getY(actionIndex)) paths.append(event.getPointerId(actionIndex),path) invalidate() } MotionEvent.ACTION_MOVE ->{ if (!paths.isEmpty()){ for (i in 0 until paths.size()){ val pointerId = event.getPointerId(i) val path = paths.get(pointerId) path.lineTo(event.getX(i),event.getY(i)) } invalidate() } } MotionEvent.ACTION_UP,MotionEvent.ACTION_POINTER_UP -> { val actionIndex = event.actionIndex val pointerId = event.getPointerId(actionIndex) paths.remove(pointerId) invalidate() } } return true } }