compose map 源码解析

目录

TileCanvas

ZoomPanRotateState

ZoomPanRotate

布局,手势处理完了,就开始要计算tile了

MapState

TileCanvasState


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
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值