百度地图缩放 — 顺滑缩放地图

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

一、解决思路

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

  (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

posted @ 2018-08-20 18:34  Spiderman.L  阅读(3131)  评论(0编辑  收藏  举报