倒霉的菜鸟

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

多点触控基本上可以分为3种类型:

接力型: 新加进来的手指控制屏幕

配合型: 多个手指共同控制屏幕

各自为战型: 多个手指各自控制屏幕

现在就分别对这三种的实现做个小结:

开始之前我们先看下单点触控怎么实现

 1 class MultiTouchView(context: Context, attributeSet: AttributeSet): View(context, attributeSet) {
 2 
 3     private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
 4     val bitmap = getBitMap(resources,100f.toPx.toInt())
 5     var offSetX = 0f
 6     var offSetY = 0f
 7     //手指落下时的位置
 8     var downX = 0f
 9     var downY = 0f
10     //初始位置
11     var originalOffSetX = 0f
12     var originalOffSetY = 0f
13 
14     override fun onDraw(canvas: Canvas) {
15         super.onDraw(canvas)
16         canvas.drawBitmap(bitmap, offSetX, offSetY, paint)
17     }
18 
19     override fun onTouchEvent(event: MotionEvent): Boolean {
20         //先看下单点触控怎么实现
21         when(event.actionMasked){
22             MotionEvent.ACTION_DOWN -> {
23                 downX = event.x
24                 downY = event.y
25                 //每次有新的action_down事件时, 把上次的offset赋值给originalOffSet
26                 originalOffSetX = offSetX
27                 originalOffSetY = offSetY
28             }
29             MotionEvent.ACTION_MOVE -> {
30                 //为了移动时让图片基于手指落下时的位置移动,我们应该给它减掉手指落下时的位置
31                 //第一次事件序列结束后, 图片可能已经移动到了某个位置, 那么下次move事件时,应该基于它来移动
32                 offSetX = originalOffSetX + event.x - downX
33                 offSetY = originalOffSetY + event.y - downY
34                 invalidate()
35             }
36         }
37         //这里必须返回true以消费事件
38         return true
39     }
40 }

现在看看接力型:接力型的关键点在于找到新加进来的那个手指, 把控制权交给它

 1 class MultiTouchView(context: Context, attributeSet: AttributeSet): View(context, attributeSet) {
 2 
 3     private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
 4     val bitmap = getBitMap(resources,100f.toPx.toInt())
 5     var offSetX = 0f
 6     var offSetY = 0f
 7     //手指落下时的位置
 8     var downX = 0f
 9     var downY = 0f
10     //初始位置
11     var originalOffSetX = 0f
12     var originalOffSetY = 0f
13     //记录新加入的手指的id
14     var newPointId = 0
15 
16     override fun onDraw(canvas: Canvas) {
17         super.onDraw(canvas)
18         canvas.drawBitmap(bitmap, offSetX, offSetY, paint)
19     }
20 
21     override fun onTouchEvent(event: MotionEvent): Boolean {
22         //多点触控这里必须用event.actionMasked而不能用event.action, 否则可能导致行为偏差
23         when(event.actionMasked){
24             MotionEvent.ACTION_DOWN -> {
25                 downX = event.x
26                 downY = event.y
27                 //每次有新的action_down事件时, 把上次的offset赋值给originalOffSet
28                 originalOffSetX = offSetX
29                 originalOffSetY = offSetY
30                 //action_down只在第一根手指加入时调用, 所以可以直接用index 0来获取id
31                 newPointId = event.getPointerId(0)
32             }
33             MotionEvent.ACTION_MOVE -> {
34                 //这里涉及到一个问题, 就是如何获取到有控制权的那根手指
35                 //我们知道每个MotionEvent它包含4个信息【x, y, index, id】
36                 //event.x其实就等于event.getX(index=0)
37                 //随着多个手指的加入和抬起, 这个index是不断变化的
38                 //因此我们只能在每次有手指加入时,记录下id, 然后通过id来找
39                 //event.findPointerIndex(newPointId)通过id来查找index
40                 offSetX = originalOffSetX + event.getX(event.findPointerIndex(newPointId)) - downX
41                 offSetY = originalOffSetY + event.getY(event.findPointerIndex(newPointId)) - downY
42                 invalidate()
43             }
44             MotionEvent.ACTION_POINTER_DOWN -> {
45                 //event.pointerCount可以获取到当前一共有多少个手指
46                 newPointId = event.getPointerId(event.actionIndex)
47                 //新加入的手指获得控制权, 那么就要先记录下新手指触摸到的位置
48                 downX = event.getX(event.actionIndex)
49                 downY = event.getY(event.actionIndex)
50                 //同理,把上次的offset赋值给originalOffSet
51                 originalOffSetX = offSetX
52                 originalOffSetY = offSetY
53             }
54             MotionEvent.ACTION_POINTER_UP -> {
55                 val actionIndex = event.actionIndex
56                 val pointId = event.getPointerId(actionIndex)
57                 //每当有手指抬起时, 要把控制权交给最后加入的那根手指
58                 //只有当抬起的手指当前拥有控制权的时候才需要操作
59                 if(pointId == newPointId){
60                     //如果它时最后一根手指
61                     val newIndex = if(actionIndex == event.pointerCount -1){
62                         event.pointerCount -2
63                     }else {
64                         event.pointerCount -1
65                     }
66                     newPointId = event.findPointerIndex(newIndex)
67                     downX = event.getX(newIndex)
68                     downY = event.getY(newIndex)
69                     originalOffSetX = offSetX
70                     originalOffSetY = offSetY
71                 }
72 
73             }
74         }
75         //这里必须返回true以消费事件
76         return true
77     }
78 }

配合型: 因为要多个手指配合, 所以这种类型的关键在于找到中心点, 每次有新手指加入/抬起的时候重新计算中心点, 移动的时候只参考中心点

获取中心点的方式就是各个手指的坐标加起来除以手指的个数

我们在第一种的基础上稍加改变:

 1 //配合型
 2 class MultiTouchView2(context: Context, attributeSet: AttributeSet): View(context, attributeSet) {
 3 
 4     private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
 5     val bitmap = getBitMap(resources,100f.toPx.toInt())
 6     var offSetX = 0f
 7     var offSetY = 0f
 8     //手指落下时的位置
 9     var downX = 0f
10     var downY = 0f
11     //初始位置
12     var originalOffSetX = 0f
13     var originalOffSetY = 0f
14 
15     override fun onDraw(canvas: Canvas) {
16         super.onDraw(canvas)
17         canvas.drawBitmap(bitmap, offSetX, offSetY, paint)
18     }
19 
20     override fun onTouchEvent(event: MotionEvent): Boolean {
21         //中心点坐标
22         val focusX: Float
23         val focusY: Float
24         //一共有几个手指
25         var pointerCount = event.pointerCount
26         var sumX = 0f
27         var sumY = 0f
28         //是否有手指抬起
29         var isPointerUp = event.actionMasked == MotionEvent.ACTION_POINTER_UP
30         for (i in 0 until pointerCount){
31             //如果不是一个手指抬起事件,就去累加
32             if (!(isPointerUp && i==event.actionIndex)){
33                 sumX += event.getX(i)
34                 sumY += event.getY(i)
35             }
36         }
37         if (isPointerUp){
38             pointerCount-- 
39         }
40         focusX = sumX/pointerCount
41         focusY = sumY/pointerCount
42         when(event.actionMasked){
43             //这里我们已经不用关注加入几根手指或者抬起几根手指了
44                 //反正每次我们都重新计算focus
45             MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_POINTER_UP-> {
46                 downX = focusX
47                 downY = focusY
48                 originalOffSetX = offSetX
49                 originalOffSetY = offSetY
50             }
51             MotionEvent.ACTION_MOVE -> {
52                 offSetX = originalOffSetX + focusX - downX
53                 offSetY = originalOffSetY + focusY - downY
54                 invalidate()
55             }
56         }
57         return true
58     }
59 }

 

第三种, 各自为战型, 比如画板, 多个手指可以同时去画,并且相互不干扰

实现这种的关键是, 我们要分别记录下每个手指的path,各自实现

比如我们要实现一个画板, 那么我们可以维护一个Map

每当有新手指加入,就在MAP里加入一个path

有手指抬起,就把这个path移除

 

posted on 2021-10-19 11:35  倒霉的菜鸟  阅读(203)  评论(0编辑  收藏  举报