对触摸事件有基础了解的情况下,如果能准确的获取自己的每个手指的相关数据,应该就能很好的多点触控在各种情况下的行为了
一、actionMasked
我们一般重写onTouchEvent
函数时都会通过event.action
来获取事件的类型。但是在处理多点触控的时候我们需要使用event.actionMasked
,因为event.action
它不支持多点
二、获取触摸点的数据
-
event.getX() event.getY()
所谓触摸点的数据,我们需要的不就是它x,y坐标呗!
在没有学习多点触摸的时候我们获取点的坐标都是通过event.getX()
和event.getY()
。看看它的内部实现
/**
* {@link #getX(int)} for the first pointer index (may be an
* arbitrary pointer identifier).
*
* @see #AXIS_X
*/
public final float getX() {
return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
实际上它等价于event.getX(0)
public final float getX(int pointerIndex) {
return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
}
不错,其实getX
和getY
是可以填参数的。pointerIndex
看到这里我想大家都明白了,实际上触摸事件的数据是存在列表里的。通过下标去获取对于的数据。而在实际的多点事件中手指的index会随着手指的按下和抬起而发生变化,并不能将数据与手指形成绑定关系;要想一直获取到对应手指的数据我们需要另寻他法
-
event.actionIndex
按下和抬起事件与MotionEvent.ACTION_MOVE
有一个特别特征,唯一性。按下和抬起事件中必定有一个手指按下或者抬起。而我们可以通过event.actionIndex
获取到它在事件列表中对应的下标。
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
val actionIndex = event.actionIndex
}
MotionEvent.ACTION_MOVE -> {
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
val actionIndex=event.actionIndex
}
}
return true
}
-
event.getPointerId
event.getPointerId
函数在多点触控可谓是至关重要,因为它可以根据下标获取到手指的id
。没错这个id
在按下是生成,在抬起时销毁,伴随一个完整的事件流程。id
与手指的事件存在绑定的关系
三、多指画图示例
多指画图.gif图很丑。。。
实现的示例很简单,用path把手指的移动轨迹保存下来,然后绘制
class MultiTouchView3(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val paths = SparseArray<Path>()//多指操作,肯定需要保存多条path
init {
paint.style = Paint.Style.STROKE
paint.strokeWidth = 4.dp
paint.strokeCap = Paint.Cap.ROUND
paint.strokeJoin = Paint.Join.ROUND
}
//把path遍历出来进行绘制就可以了
override fun onDraw(canvas: Canvas) {
for (i in 0 until paths.size()) {
canvas.drawPath(paths.valueAt(i), paint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
}
需要注意的是paths.valueAt
函数,我们通过下标去获取对应的path。而不是使用paths.get()
,以为get
的参数是key
并不是下标。key
就是我们的id
,唯一的
/**
* Gets the Object mapped from the specified key, or <code>null</code>
* if no such mapping has been made.
*/
public E get(int key) {
return get(key, null);
}
如何生成path并添加数据
- 在按下事件新建path并添加到paths
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
//添加新的路径 key是id
val actionIndex = event.actionIndex
val id = event.getPointerId(actionIndex)
val path = Path()
path.moveTo(event.getX(actionIndex), event.getY(actionIndex))
paths.append(id, path)
}
- 在抬起事件中去删除path
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
//删除路径,key是id
paths.remove(event.getPointerId(event.actionIndex))
invalidate()
}
- 在MOVE事件中去根据id向对应的path添加轨迹数据
MotionEvent.ACTION_MOVE -> {
//根据id去添加路径
for (i in 0 until event.pointerCount) {
val pointerId = event.getPointerId(i)
paths[pointerId].lineTo(event.getX(i), event.getY(i))
}
invalidate()
}
完整的代码
class MultiTouchView3(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val 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) {
for (i in 0 until paths.size()) {
canvas.drawPath(paths.valueAt(i), paint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
//添加新的路径 key是id
val actionIndex = event.actionIndex
val id = event.getPointerId(actionIndex)
val path = Path()
path.moveTo(event.getX(actionIndex), event.getY(actionIndex))
paths.append(id, path)
}
MotionEvent.ACTION_MOVE -> {
//根据id去添加路径
for (i in 0 until event.pointerCount) {
val pointerId = event.getPointerId(i)
paths[pointerId].lineTo(event.getX(i), event.getY(i))
}
invalidate()
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
//删除路径,key是id
paths.remove(event.getPointerId(event.actionIndex))
invalidate()
}
}
return true
}
}
网友评论