在剛接入地圖後,發現地圖的縮放在手指離開後就戛然而止,這和人家的體驗不太同樣啊。有點尬,開始解決一下這個問題。文章代碼是在上一篇代碼的基礎上,若是有什麼疑問能夠看下上一篇html
1、解決思路java
讓地圖在手指離開後不立馬中止縮放,而是繼續縮放必定的比例後中止。何時中止?在手指離開後到中止這段時間爲百度地圖設置的縮放值如何變化?基於這兩個問題,如今有兩種思路:less
(1)本身指定繼續縮放的次數與時間,經過 Handler 實現:(這個是開發同一項目的一位同事的思路)ide
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)慣性的平滑縮放地圖post
縮放地圖的動做大小就是指滑動速度v,v越大,慣性越大,可是它慢慢的會變爲零。符合這個特性的,上一張圖你就明白:(注意在手勢操做期間並不必定是按照這個函數,咱們只是經過兩個座標模擬後續的值,實現平滑縮放)ui
仔細看一下圖片就知道: 縮放 z 與 時間 t 是一個一元二次方程: z = at2 + bt + c ,對其求導可得:v = 2at + b,v=0 時,tm = -b/2a 。假如咱們已經解出這個方程,而後能夠獲得 tm . 讓地圖在 t2 到 tm 這段時間內按照對應的函數值 z 進行縮放便可。怎麼樣是否是很奶思,如今剩下的問題就是如何解這個方程了。看看咱們目前有什麼:this
1. 兩個點的座標,地圖開始縮放的時間與縮放值 (t1,z1) ; 地圖結束縮放後的時間與縮放值 (t2,z2) 注:(tm,zm) 爲咱們要慣性的平滑縮放地圖最終的結束點spa
2. v = 2at + b ,因此 a 是一個加速度 ,這個能夠由咱們本身來決定。因此 a 也拿到了。code
兩個座標一個 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