zoukankan      html  css  js  c++  java
  • 百度地图缩放 — 顺滑缩放地图

      在刚接入地图后,发现地图的缩放在手指离开后就戛然而止,这和人家的体验不太一样啊。有点尬,开始解决一下这个问题。文章代码是在上一篇代码的基础上,如果有什么疑问可以看下上一篇

    一、解决思路

      让地图在手指离开后不立马停止缩放,而是继续缩放一定的比例后停止。什么时候停止?在手指离开后到停止这段时间为百度地图设置的缩放值如何变化?基于这两个问题,现在有两种思路:

      (1)自己指定继续缩放的次数与时间,通过 Handler 实现:(这个是开发同一项目的一位同事的思路)

    
    
    val none = 0  //初始
    val large = 1 //放大
    val small = -1 //缩小
    var mode = 0

    baiduMap.setOnMapTouchListener { val pointerCount
    = it.pointerCount when (it.action) { MotionEvent.ACTION_DOWN -> { mapView.map.uiSettings.isScrollGesturesEnabled = true } MotionEvent.ACTION_MOVE -> { if (pointerCount >= 2) { if (fingersStep == null) { fingersStep = (it.getX(0) - it.getX(1)) * (it.getX(0) - it.getX(1)) + (it.getY(0) - it.getY(1)) * (it.getY(0) - it.getY(1)) } val temp = (it.getX(0) - it.getX(1)) * (it.getX(0) - it.getX(1)) + (it.getY(0) - it.getY(1)) * (it.getY(0) - it.getY(1)) val size = temp - fingersStep!! mode = when { size == 0f -> none size > 0 -> large else -> small } fingersStep = temp mapView.map.uiSettings.isScrollGesturesEnabled = false } } MotionEvent.ACTION_UP -> { handler.postDelayed({ mapView.map.uiSettings.isScrollGesturesEnabled = true }, 500) when (mode) { small, large -> { letMapSmallerOrLarger(mode) } } fingersStep = null mode = none } } }
    /**
         * 地图放大或者缩小
         */
        private fun letMapSmallerOrLarger(state: Int) {
            var zoom: Float
            handler.postDelayed({
                zoom = mapChangeStatus.zoom
                zoom += (0.5f * state)
                baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLngZoom(mapStatus.value!!.target, zoom))
                handler.postDelayed({
                    zoom += (0.3f * state)
                    baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLngZoom(mapStatus.value!!.target, zoom))
                    handler.postDelayed({
                        zoom += (0.1f * state)
                        baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLngZoom(mapStatus.value!!.target, zoom))
                    }, 96)
                }, 64)
            }, 32)
        }

      这种思路也可以在视觉上展现平滑的效果,但是有一个缺点,那就是不管是你操作的动作有多大,它的平滑效果都是一样的。就像你演小品和宋小宝演小品,观众的反应是一样的。这就涉及到一个词 —— 惯性,下面这种思路就是惯性的平滑缩放地图

      (2)惯性的平滑缩放地图

      缩放地图的动作大小就是指滑动速度v,v越大,惯性越大,但是它慢慢的会变为零。符合这个特性的,上一张图你就明白:(注意在手势操作期间并不一定是按照这个函数,我们只是通过两个坐标模拟后续的值,实现平滑缩放)

                                   

      仔细看一下图片就知道: 缩放 z 与 时间 t 是一个一元二次方程: z = at2 + bt + c ,对其求导可得:v = 2at + b,v=0 时,tm = -b/2a 。假如我们已经解出这个方程,然后可以得到 tm . 让地图在 t2 到 tm 这段时间内按照对应的函数值 z 进行缩放即可。怎么样是不是很奶思,现在剩下的问题就是如何解这个方程了。看看我们目前有什么:

      1. 两个点的坐标,地图开始缩放的时间与缩放值  (t1,z1) ; 地图结束缩放后的时间与缩放值 (t2,z2)    注:(tm,zm) 为我们要惯性的平滑缩放地图最终的结束点

      2. v = 2at + b ,所以 a 是一个加速度 ,这个可以由我们自己来决定。所以 a 也拿到了。

      两个坐标一个 a ,那 b、c 是不是都出来了哈。通过图片也可以看出缩小和放大是有区别的。下面我们就看代码吧。

    class MapOverlayLayout(context: Context?, attrs: AttributeSet?) : RelativeLayout(context, attrs) {
    
        private var mMapView: MapView? = null
        private var isMutilPoint = false
        private var isOnePoint = true
        private lateinit var startZoomPair: Pair<Long, Float>
        private var endZoom = Variable<Pair<Long, Float>>()
    
        init {
            startSlideScaleMap()
        }
    
        override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
            super.onLayout(changed, l, t, r, b)
            if (changed)
                setup()
        }
    
        private fun setup() {
            if (mMapView != null)
                return
            for (i in 0 until childCount) {
                val child = getChildAt(i)
                if (child != null && child is MapView) {
                    mMapView = child
                    break
                }
            }
            if (childCount > 0 && mMapView == null)
                Log.e(this.javaClass.simpleName, "未将地图放置在子节点下")
        }
    
        fun setBaiduMap(mapView: MapView) {
            mMapView = mapView
        }
    
        override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
            var isIntercept = false
            when (ev!!.action) {
                MotionEvent.ACTION_DOWN -> {
                    startZoomPair = Pair(System.currentTimeMillis() % 100000, mMapView!!.map.mapStatus.zoom)
                }
                MotionEvent.ACTION_MOVE -> {
                    mMapView!!.map.uiSettings.isScrollGesturesEnabled = !isMutilPoint && isOnePoint
                    if (ev.pointerCount >= 2) {
                        isMutilPoint = true
                    }
                    if (ev.pointerCount == 1) {
                        isOnePoint = true
                    }
                }
                MotionEvent.ACTION_UP -> {
                    if (isMutilPoint) {
                        endZoom new Pair(System.currentTimeMillis() % 100000, mMapView!!.map.mapStatus.zoom)
                    }
                    isMutilPoint = false
                    isOnePoint = true
                }
            }
            return isIntercept
        }
    
        private fun startSlideScaleMap() {
            endZoom.subscribeOn(Schedulers.computation())
                    .subscribe {
                        if (mMapView == null) {
                            Log.e(this.javaClass.simpleName, "未将地图放置在子节点下")
                            return@subscribe
                        }
                        var t1 = startZoomPair.first.toFloat()
                        val zoom1 = startZoomPair.second
                        var t2 = it.first.toFloat()
                        val temp = (t2 - t1) / 1000
                        t1 = 0f
                        t2 = (t1 + temp)
                        val zoom2 = it.second
                // 判断是缩小还是放大,设置 a 值 val a
    = if (zoom2 > zoom1) -3 else 3 val b = (zoom2 - zoom1) / (t2 - t1) - a * (t1 + t2) val c = zoom1 val tStirless = -b / (2 * a) val tMove = tStirless - t2 val MAP_ZOOM_NUMS = 10 val unitZoomLevelDuringTime = tMove / MAP_ZOOM_NUMS var lastZoom = zoom2 for (i in 1..MAP_ZOOM_NUMS) { val tempT = t2 + i * unitZoomLevelDuringTime val temZoom = a * tempT * tempT + b * tempT + c var duty: Float if (lastZoom > zoom1 && temZoom > lastZoom) { duty = temZoom - lastZoom if (lastZoom - zoom2 > 1) return@subscribe mMapView!!.map.animateMapStatus(MapStatusUpdateFactory.zoomBy(duty)) lastZoom = temZoom } if (lastZoom < zoom1 && temZoom < lastZoom) { duty = temZoom - lastZoom if (zoom2 - lastZoom > 1) return@subscribe mMapView!!.map.animateMapStatus(MapStatusUpdateFactory.zoomBy(duty)) lastZoom = temZoom } } } } }

      代码说明一下,在 onInterceptTouchEvent 获取两个坐标点的值,起始点坐标 startZoomPair ,结束点项目中通过 rxjava 观测结束点变化 ,方程函数操作在 startSlideScaleMap() 方法中 ,代码中时间基点取为了 0 ,所时间 t1 在运算时为 0 , t2 为其差值

      昨天下班匆忙,今天扫个尾。对于已经实现了地图的平滑缩放,我们可以通过调整 a 的值来控制顺滑缩放的速度,以及加一些判断控制平滑缩放的缩放层级。结束了哈,工作愉快。附上借鉴的一片文章:

      https://www.aliyun.com/jiaocheng/355235.html

      以及MapOverlayLayout源码: 链接: https://pan.baidu.com/s/1GqZQ7wgozC0gDbizLrLHEw 密码: 2hqp

  • 相关阅读:
    MySQL——SELECT
    启动 MySQL
    Ethernet and ARP 及Wireshark实验
    ICMP 协议及Wireshark实验
    Wireshark实验——IP 协议
    关键路径
    用树结构存储的图博客(笑)
    拓扑排序
    云计算部署的未来趋势将从自动化转向为自主化
    苹果拥抱 Rust,正在将 C 代码移植到 Rust
  • 原文地址:https://www.cnblogs.com/aimqqroad-13/p/9507235.html
Copyright © 2011-2022 走看看