目录
telephoto的源码已经分析过了.它的封装好,扩展好,适用于各种view.
最近又看到一个用compose写的map,用不同的方式,有点意思.分析一下它的实现流程与原理.
https://github.com/p-lr/MapCompose.git 这是源码.
TileCanvas
Canvas(
modifier = modifier
.fillMaxSize()
) {
withTransform({
translate(left = -zoomPRState.scrollX, top = -zoomPRState.scrollY)
scale(scale = zoomPRState.scale, Offset.Zero)
}) {
for (tile in tilesToRender) {
val bitmap = tile.bitmap ?: continue
val scaleForLevel = visibleTilesResolver.getScaleForLevel(tile.zoom)
?: continue
val tileScaled = (tileSize / scaleForLevel).toInt()
val l = tile.col * tileScaled
val t = tile.row * tileScaled
val r = l + tileScaled
val b = t + tileScaled
dest.set(l, t, r, b)
drawIntoCanvas {
it.nativeCanvas.drawBitmap(bitmap, null, dest, null)
}
}
}
}
我把旋转的代码删除了.telephoto是在绘制前把偏移量计算好了,而这个绘制是得到tile后,在绘制的时候设置偏移量,使用的是bitmap,所以不支持多平台.
使用画布前要初始化一些数据:
val zoomPRState = state.zoomPanRotateState
key(state) {
ZoomPanRotate(
modifier = modifier
.clipToBounds()
.background(state.mapBackground),
gestureListener = zoomPRState,
layoutSizeChangeListener = zoomPRState,
) {
TileCanvas(
modifier = Modifier,
zoomPRState = zoomPRState,
visibleTilesResolver = state.visibleTilesResolver,
tileSize = state.tileSize,
tilesToRender = state.tileCanvasState.tilesToRender,
)
content()
}
}
zoomPRState最重要的就是这个,保存了各种状态,变量.它的初始化就要追溯到mapstate了.因为它是用于map,所以它会先设置一个地图的总大小,层级.
val state = MapState(4, 4096, 4096) {
scale(1.0f)
}.apply {
addLayer(tileStreamProvider)
shouldLoopScale = true
//enableRotation()
}
TileStreamProvider就是根据这个缩放级别,行列去获取对应的图片,示例中是使用assets里面的图片,都是放好切片的.
ZoomPanRotateState
这个类管理了缩放,平移,旋转功能.
看一个缩放功能,先把缩放值作一个范围取值,避免超出最大与最小值.然后更新中心点,再通知监听者
fun setScale(scale: Float, notify: Boolean = true) {
this.scale = constrainScale(scale)
updateCentroid()
if (notify) notifyStateChanged()
}
中心点的计算,取当前布局的大小的半的值,加上偏移滚动的量
private fun updateCentroid() {
pivotX = layoutSize.width.toDouble() / 2
pivotY = layoutSize.height.toDouble() / 2
centroidX = (scrollX + pivotX) / (fullWidth * scale)
centroidY = (scrollY + pivotY) / (fullHeight * scale)
}
这个state它不是自己更新的,是外部触发的,哪里触发先放着.
缩放有了,滚动的与缩放类似.需要确定边界值,然后更新中心点.
fun setScroll(scrollX: Float, scrollY: Float) {
this.scrollX = constrainScrollX(scrollX)
this.scrollY = constrainScrollY(scrollY)
updateCentroid()
notifyStateChanged()
}
这三个方法是这个类的核心方法,其它都是通过它们计算得到的.
ZoomPanRotate
这算是入口页面,自定义了Layout,它与普通的图片不同,它是多层级别的,所以要自定义一个.
它的布局大小改变的时,通过layoutSizeChangeListener,告诉zoomPanRotateState,
override fun onSizeChanged(composableScope: CoroutineScope, size: IntSize) {
scope = composableScope
var newScrollX: Float? = null
var newScrollY: Float? = null
if (layoutSize != IntSize.Zero) {
newScrollX = scrollX + (layoutSize.width - size.width) / 2
newScrollY = scrollY + (layoutSize.height - size.height) / 2
}

最低0.47元/天 解锁文章
514

被折叠的 条评论
为什么被折叠?



