android高德SDK,Marker的zIndex导致的遮罩层问题解决

本文介绍了在Android中使用高德SDK遇到Marker的zIndex无法正确实现遮罩层覆盖的问题,并详细阐述了通过实现CustomRenderer接口,结合OpenGLES进行自定义渲染以解决此问题的方法,包括遮罩层、路径和Marker的绘制策略,以及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题描述

最近项目接到了一个遮罩层需求,效果图如下
在这里插入图片描述
凭感觉会觉得不难,遮罩层+遮罩层之上的元素,通过高德自己的api进行图层排序就可以。
假如用Polygon来绘制遮罩层,那么测试代码如下:

      val bottomMarkerPosition = LatLng(29.0, 114.0)
        val topMarkerPosition = LatLng(29.05, 114.05)
        val maskLayerPath = listOf(
            LatLng(28.9, 113.9), LatLng(28.9, 114.1), LatLng(29.1, 114.1),
            LatLng(29.1, 113.9)
        )
        val bounds =
            LatLngBounds.builder().include(maskLayerPath[0]).include(maskLayerPath[2]).build()
        map.map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
        val maskLayer = map.map.addPolygon(PolygonOptions().also {
            it.points = maskLayerPath
            it.fillColor(Color.parseColor("#A0000000"))
            it.zIndex(50f)
        })
        val bottomMarker = map.map.addMarker(MarkerOptions().position(bottomMarkerPosition))
            .also {
                it.zIndex = 1f
            }
        val topMarker = map.map.addMarker(MarkerOptions().position(topMarkerPosition))
            .also {
                it.zIndex = 100f
            }

上述测试代码,通过zIndex对遮罩层之上,之下的Marker都进行了排序,但实际的运行效果:
在这里插入图片描述
遮罩层并没有如预期一般将下层Marker挡住。
产生这个问题的原因也很简单,下面是官方工作人员对我的工单回复:
在这里插入图片描述
所以简单的说,通过Marker、Polygon或者自定义TileProvider等方式来添加可以明确区分上下层的遮罩层都是不可行的,或者说无法达到比较完美的效果。

  • 如果用Polygon实现遮罩,则遮罩层无法遮挡住Marker,即使通过混合遮罩层颜色的方法让下层Marker看起来像被遮挡了,只要这个Marker和某个上层非Marker元素存在重叠,则这个看起来应该在下层的Marker还是会挡住上层元素。
  • 如果用Marker实现遮罩,只要遮罩层上有非Marker元素,那么显然该元素会被遮罩层盖住,不符合需求。
  • 通过自定义TileProvider等方式实现遮罩层的问题同Polygon。

综上,在常规api无法实现的情况下,最终我采用OpenGLES+AMap.setCustomRenderer方式来实现,以此绕过了该问题。

二、CustomRenderer接口解决方案

com.amap.api.maps.CustomRenderer接口是高德提供的一个自己的接口,该接口继承于android.opengl.GLSurfaceView.Renderer。
当有一个自己的CustomRenderer对象时,可以通过aMap.setCustomRenderer()方法,将该对象传递给高德。从而实现在高德地图上的自定义的OpenGLES渲染逻辑。
通过该接口绘制的图形始终位于高德地图组件中的最上层,也就是这一点构成了我们解决前面提到问题的基础。
另一方面,通过aMap对象,我们可以获得高德地图的projectionMatrix和viewMatrix(在官方文档中并没有提到,但实际上是可以调用到的,当然,因此也要注意SDK版本号,这两个属性理论上应该各版本都是存在的,因为高德一个官方demo中直接有这两个属性的调用,如果这两个属性不允许访问的话,官方demo也不应该把它们放进去)。借助于这两个矩阵,我们就可以实现地理信息数据到OpenGLES坐标系的位置转换。
所以综上,只要自己用OpenGLES实现遮罩层和遮罩层之上数据的绘制,就可以实现效果较好的遮罩层。但显然该方法也是存在缺点的,如果遮罩层之上的元素比较复杂,那么无疑会写的很麻烦,其次交互事件也需要自己处理。
我的项目中由于只需要在遮罩层上绘制路径和两个Marker,所以大概的代码如下:

遮罩层绘制:

private class GLMask {
    var color = GLColor(Color.parseColor("#A0000000"))
    private val matrix = FloatArray(16).also {
        Matrix.setIdentityM(it, 0)
    }
    private val indicesBuffer = shortArrayOf(0, 3, 1, 1, 3, 2).toBuffer()
    private val indicesCount = 6
    private val vertexBuffer = floatArrayOf(
        -1f, -1f, 0f,
        1f, -1f, 0f,
        1f, 1f, 0f,
        -1f, 1f, 0f
    ).toBuffer()

    fun drawMask(vertexLoc: Int, colorLoc: Int, matrixLoc: Int) {
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glUniformMatrix4fv(matrixLoc, 1, false, matrix, 0)
        glUniform4f(colorLoc, color.r, color.g, color.b, color.a)
        glEnableVertexAttribArray(vertexLoc)
        glVertexAttribPointer(
            vertexLoc, 3, GL_FLOAT, false, 0, vertexBuffer
        )
        glDrawElements(GL_TRIANGLES, indicesCount, GL_UNSIGNED_SHORT, indicesBuffer)
        glDisable(GL_BLEND)
        glDisableVertexAttribArray(vertexLoc)
    }
}

遮罩层绘制时,它的顶点数组不用动态计算,固定为代码中vertexBuffer对应的顶点数组就好,这样刚好对应整个Map。简单来说就是在Map上画一个刚好和Map一样大小的矩形就完事。
路径绘制的代码略过,反正就是画线就行。
起点和终点的两个Marker,可以直接利用高德自己的BitmapDescriptor对象。通过BitmapDescriptorFactory的静态方法获得一个该对象,然后通过该对象可以取得一个Bitmap,所以直接把这个Bitmap画到合适的位置就好。

private class GLBitmap(private val aMap: AMap) {
    var icon: BitmapDescriptor? = null
    var position: LatLng? = null
    var anchor = PointF(0.5f, 1f)
    private var width = 0
    private var height = 0
    private var textureId: Int? = null
    private var scaledWidth = 0f
    private var scaledHeight = 0f
    private var bitmapWidth = 0
    private var bitmapHeight = 0
    private lateinit var vertexBuffer: FloatBuffer
    private val indicesBuffer = shortArrayOf(0, 3, 1, 1, 3, 2).toBuffer()
    private val uvBuffer = floatArrayOf(
        0f, 1f,
        1f, 1f,
        1f, 0f,
        0f, 0f
    ).toBuffer()

    private fun update() {
        if (icon == null) {
            if (textureId != null) {
                glDeleteTextures(1, intArrayOf(textureId!!), 0)
                textureId = null
            }
            return
        }
        if (position == null) return
        genTexture()
        scaledWidth = aMap.projection.toOpenGLWidth(bitmapWidth)
        scaledHeight = aMap.projection.toOpenGLWidth(bitmapHeight)
        if (width == 0 || height == 0) return
        val basePoint = aMap.projection.toScreenLocation(position)
        val glX = (2f * basePoint.x - width) / width
        val glY = (2f * (height - basePoint.y) - height) / height
        val glW = bitmapWidth * 2f / width
        val glH = bitmapHeight * 2f / height
        val left = glX - glW * anchor.x
        val right = left + glW
        val bottom = glY + glH * anchor.y - glH
        val top = bottom + glH
        val vertices = floatArrayOf(
            left, bottom, 0f,
            right, bottom, 0f,
            right, top, 0f,
            left, top, 0f
        )
        vertexBuffer = vertices.toBuffer()
    }

    private fun genTexture() {
        if (textureId == null) {
            bitmapWidth = icon!!.width
            bitmapHeight = icon!!.height
            val intArray = IntArray(1)
            glGenTextures(1, intArray, 0)
            textureId = intArray[0]
            glBindTexture(GL_TEXTURE_2D, textureId!!)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
            GLUtils.texImage2D(GL_TEXTURE_2D, 0, icon!!.bitmap, 0)
        }
    }

    // index用于确定texture所绑定的位置
    fun drawBitmap(
        vertexLoc: Int, textureLoc: Int, uvLoc: Int,
        index: Int, width: Int, height: Int
    ) {
        this.width = width
        this.height = height
        update()
        if (textureId == null) return
        glEnableVertexAttribArray(vertexLoc)
        glVertexAttribPointer(
            vertexLoc, 3, GL_FLOAT, false, 0, vertexBuffer
        )
        glEnableVertexAttribArray(uvLoc)
        glVertexAttribPointer(
            uvLoc, 2, GL_FLOAT, false, 0, uvBuffer
        )
        glActiveTexture(GL_TEXTURE0 + index)
        glBindTexture(GL_TEXTURE_2D, textureId!!)
        glUniform1i(textureLoc, index)
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indicesBuffer)
        glDisableVertexAttribArray(vertexLoc)
        glDisableVertexAttribArray(uvLoc)
    }

    fun assignSize(width: Int, height: Int) {
        this.width = width
        this.height = height
    }

    fun remove() {
        glDeleteTextures(1, intArrayOf(textureId!!), 0)
        icon = null
        textureId = null
    }
}

高德的Marker具有一个特性,它始终是面向屏幕的,不会随着地图旋转而旋转,所以自己画Marker的时候也要注意这一点。

三、总结

最终的实现效果:
在这里插入图片描述
综上,对于高德Android SDK,Marker的zIndex导致的遮罩层问题,在上层元素不复杂的情况下,完全可以通过OpenGLES+CustomRenderer接口来解决。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值