• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Android 自定义View 处理多点触控,实现双指支持同时缩放平移

武飞扬头像
i听音乐的猿
帮助2

前言

最近学习了下Android 多点触控,碰巧前段时间写了绘制地理坐标的实现思路,在Window上实现了功能,居于这个思路,想在Android平台实现,这其中涉及到屏幕触摸事件转换成地图平移缩放的过程,刚搞懂,做个笔记记录下。

  1. 参考Android官方说明文档:拖动和缩放
  2. 使用的原生绘图方式 实现绘制地理坐标
  3. Demo地址

实现代码

class TouchController {
    private val MIN_THRESHOLD = 2

    var touchListener: TouchMapListener? = null

    val zoomCenterPoint = doubleArrayOf(0.0, 0.0)
    val translateStartPoint = doubleArrayOf(0.0, 0.0)

    var lastDistance = 0.0
    //通过官方文档描述,需要记录下当前的默认手指Id
    private var mActivePointerId = INVALID_POINTER_ID
     fun onTouch(event: MotionEvent): Boolean {
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
            //第一个手指按下,把这个Id记录下来,并且把其坐标作为起始点击坐标
                event.actionIndex.also { pointerIndex ->
                    val findPointerIndex = event.findPointerIndex(pointerIndex)
                    translateStartPoint[0] = event.getX(findPointerIndex).toDouble()
                    translateStartPoint[1] = event.getY(findPointerIndex).toDouble()
                }

                mActivePointerId = event.getPointerId(0)
            }
           MotionEvent.ACTION_POINTER_DOWN -> {
           //第二个以上手指按下,计算两个手指之间的中心位置,因为我们要实现缩放的同时支持平移,
           //所以,一个手指按下时,平移的基点就是这个手指的坐标,两个手指按下时,我们就以两个手指的中心
           //点坐标作为基点,同时也以这个基点作为缩放的基准点
                calculateCenter(event).also {
                    zoomCenterPoint[0] = it[0]
                    zoomCenterPoint[1] = it[1]
                    translateStartPoint[0] = it[0]
                    translateStartPoint[1] = it[1]
                }
                //计下按下时两个手指的直线距离,为双指缩放准备
                lastDistance = spacing(event)
            }
            MotionEvent.ACTION_POINTER_UP -> {
            //多点触摸的时候,当有一个手指抬起时,会触发这个Action,我们需要判断,抬起的手指是不是
            //当前记录的第一个手指的Id,如果是,说明默认的手指Id已经发生变化,变成另一个了,所以
            //我们的平移基准点坐标也要改变成变成另一个,否则会出现松开第一种手指时,没有进行拖拽却计算到很大的平移距离的问题,这里也是参考官方文档解释后发现的问题
                event.actionIndex.also { pointerIndex ->
                    val pointerId = event.getPointerId(pointerIndex)
                    if (pointerId == mActivePointerId) {
                        val newPointerIndex = if (pointerIndex == 0) 1 else 0
                        translateStartPoint[0] = event.getX(newPointerIndex).toDouble()
                        translateStartPoint[1] = event.getY(newPointerIndex).toDouble()
                        //重新设置默认手指的id 保证取的坐标没问题
                        mActivePointerId = event.getPointerId(newPointerIndex)
                    } else {
                    //松开的不是第一个按下的手指,直接把基准点重置成第一个手指的坐标
                        translateStartPoint[0] =
                            event.getX(event.findPointerIndex(mActivePointerId)).toDouble()
                        translateStartPoint[1] =
                            event.getY(event.findPointerIndex(mActivePointerId)).toDouble()
                    }
                }
            }
            MotionEvent.ACTION_MOVE -> {
                val translateEndPoint = doubleArrayOf(0.0, 0.0)
                //拖放结束点先用默认手指的当前位置
                event.findPointerIndex(mActivePointerId).let { pointerIndex ->
                    translateEndPoint[0] = event.getX(pointerIndex).toDouble()
                    translateEndPoint[1] = event.getY(pointerIndex).toDouble()
                }
                //如果多点触控,我们要计算两个手指的中心点作为终点
                if (event.pointerCount >= 2) {
                    calculateCenter(event).also {
                        zoomCenterPoint[0] = it[0]
                        zoomCenterPoint[1] = it[1]

                        translateEndPoint[0] = it[0]
                        translateEndPoint[1] = it[1]
                    }

                    val currentDistance = spacing(event)
                    val distanceDiff = currentDistance - lastDistance
                    if (abs(distanceDiff) > MIN_THRESHOLD) {
                    //把事件传给地图去控制比例尺缩放
                        if (distanceDiff < 0) {
                            touchListener?.onZoomIn(zoomCenterPoint[0], zoomCenterPoint[1])
                        } else {
                            touchListener?.onZoomOut(zoomCenterPoint[0], zoomCenterPoint[1])
                        }
                        lastDistance = currentDistance
                    }
                }
	                //计算x、y方向的平移量
	              	val xDiff = translateEndPoint[0] - translateStartPoint[0]
	                val yDiff = translateEndPoint[1] - translateStartPoint[1]
	
	                if (abs(xDiff) > MIN_THRESHOLD || abs(yDiff) > MIN_THRESHOLD) {
	                	//把事件传给地图去控制平移
	                    touchListener?.onDrag(xDiff, yDiff)
	                }
	              	translateStartPoint[0] = translateEndPoint[0]
	                translateStartPoint[1] = translateEndPoint[1]
            }
            MotionEvent.ACTION_CANCEL,
            MotionEvent.ACTION_UP -> {
                mActivePointerId = INVALID_POINTER_ID
            }
        }
        return true 
	}

  /**
     * 计算连个手指连线中心点
     */
    private fun calculateCenter(event: MotionEvent): DoubleArray {
        //计算起点中心坐标
        val x0 = event.getX(0)
        val y0 = event.getY(0)

        val x1 = event.getX(1)
        val y1 = event.getY(1)

        return doubleArrayOf(
            x0   (x1 - x0) / 2.0,
            y0   (y1 - y0) / 2.0
        )
    }
    
 	/**
     * 计算两个点的距离
     *
     * @param event
     * @return
     */
    private fun spacing(event: MotionEvent): Double {
        return if (event.pointerCount == 2) {
            val x = event.getX(0) - event.getX(1)
            val y = event.getY(0) - event.getY(1)
            sqrt((x * x   y * y).toDouble())
        } else 0.0
    }


  interface TouchMapListener {
        fun onDrag(xDiff: Double, yDiff: Double)
        fun onZoomIn(centerX: Double, centerY: Double)
        fun onZoomOut(centerX: Double, centerY: Double)
    }
学新通

至此,缩放平移核心逻辑代码已完成编写,对接之前写得绘图Demo,在地理坐标(100,100)附近随机绘制了1000隔点,测试效果如下:
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbkkci
系列文章
更多 icon
同类精品
更多 icon
继续加载