【问题标题】:Android Jetpack Composable Draw Large List of coordinatesAndroid Jetpack 可组合绘制大坐标列表
【发布时间】:2022-01-16 08:05:27
【问题描述】:

我有一个包含 36000 个坐标的列表,我需要在画布上以点的形式绘制。坐标不断变化,所以我将它们作为我的视图模型的一部分保存在地图中

mutableStateOf(HashMap<Int, AtomicInteger>()) 和 AtomicIntegers 会不断更新。

地图是角度和距离,我将其转换为画布上的坐标。这行得通,但我的问题是这是否是绘制它的最有效方法 - 不断迭代地图似乎效率低下

@Composable
fun lidarComposable(vm: MainViewModel) {
    MaterialTheme {
        Column {
            Canvas(modifier = Modifier.size(500.dp, 500.dp)) {
                drawRect(Color.LightGray, topLeft = Offset(0f, 0f), size = Size(this.size.width, this.size.height))
                drawCircle(Color.Blue, 20F, this.center)
                vm.lidardata.data.forEach { (a, d) ->
                        val angle: Double = a.toDouble() / Lidar.relevence
                        val distance: Double = (d.get() / Lidar.relevence).toDouble()
                        val fixed = angle - vm.compass
                        val rad = fixed * PI / 180
                        val xx = this.center.x + (distance * cos(rad))
                        val yy = this.center.y + (distance * sin(rad))
                        drawCircle(Color.Red, 5F, Offset(xx.toFloat(), yy.toFloat()))
                }
            }
        }
    }
}

结果是一个灰色的画布,中心有一个蓝色圆圈,我想要它们的点,似乎必须有更好的方法。

从旋转的激光雷达收集数据一段时间后,我最终得到了一张非常漂亮的房间图片,但我是新手,想知道是否有更好的方法将更改推送到画布。

【问题讨论】:

  • 您是否只对更新那些在地图上发生变化的点感兴趣?地图是如何使用新值更新的?
  • 是的,这是 LIDAR 数据(一个旋转的 IR 光束持续距离目标读数) - 后台线程正在用新的距离更新我的地图,最终以点绘制 LIDAR 周围的图片像雷达显示器。我真正想要的是当它改变时显示的最新角度/距离点。我发布的内容有效,但有点笨拙。
  • 但是您将数据放入地图的具体程度如何?这些值是从单一来源检索并添加到地图还是从不同来源添加?换句话说,您的地图是从不同来源异步更新还是自动更新?当条目只有方向和角度时,如何更新地图也不清楚。没有唯一标识点的 ID。如果您可以唯一标识每个点,则有多种方法可以在不遍历整个数据的情况下获得更好的性能。
  • 这是从 MQTT 代理进入我的 android 应用程序的异步数据,因此每秒数百个异步线程在我的 VM 中设置地图的值,即 Map - 我试图保持在我的虚拟机中预加载了 AtomicIntegers 的地图,因为我正在处理很多并发问题 - 我愿意接受建议。
  • 我还将我的角度(如 90.52)转换为 9052,并将其用作地图中的键,因此 360.00 度为我提供了一张包含 36000 个对象角度条目的地图 - 似乎是一个好方法 -如果您能指出如何做到这一点,那将非常有帮助:“如果您可以唯一地识别每个点,则有多种方法可以在不遍历整个数据的情况下获得更好的性能。”

标签: android mvvm android-jetpack-compose


【解决方案1】:

我通过预加载一个点列表来过滤掉过时值、0 和无用的大数字等噪音。这段代码可以满足我的需要,我可以看到物理对象出现并随着激光雷达以罗盘方向在空间中移动而发生变化。甚至将路点显示为红色,一条蓝线显示磁北,在屏幕截图中我什至可以看到我的肩膀和弯曲的显示器。


@Composable
fun lidarComposable(vm: MainViewModel) {

    val maxRelevantAge = 5000

    MaterialTheme {
        Column {
            Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
                val padding = 50
                val canvasWidth = size.width - padding
                val canvasHeight = size.height - padding

                drawRect(Color.LightGray, topLeft = Offset(0f, 0f), size = Size(this.size.width, this.size.height))
                drawCircle(Color.Blue, 20F, this.center)

                val north = 360 - vm.compass
                rotate(north) {
                    drawLine(
                        Color.Blue,
                        this.center,
                        Offset(this.center.x, (canvasHeight / 2 - canvasWidth / 2) + padding / 2)
                    )
                    drawCircle(Color.Blue, canvasWidth / 2, this.center, style = Stroke(5f))
                }

                rotate(vm.compass) {
                    drawLine(
                        Color.Red,
                        this.center,
                        Offset(this.center.x, (canvasHeight / 2 - canvasWidth / 2) + padding / 2)
                    )
                }

                    val obstructions: MutableList<Offset> = ArrayList()
                    val surroundings: MutableList<Offset> = ArrayList()
                    vm.lidardata.data
                    vm.lidardata.data.forEach { (a, d) ->

                        if (System.currentTimeMillis() - d.timestamp.get() < maxRelevantAge) {
                            val angle: Double = a.toDouble() / Lidar.relevance
                            val distance: Double = ((d.distance.get() / Lidar.relevance).toDouble() / 2)
                            val fixed = angle - vm.compass
                            val rad = fixed * PI / 180
                            val xx = this.center.x + (distance * cos(rad))
                            val yy = this.center.y + (distance * sin(rad))

                            if (distance > 0 && ((angle < 20) or (angle > 340))) {
                                obstructions.add(Offset(xx.toFloat(), yy.toFloat()))
                            } else if (distance < canvasWidth / 2) {
                                surroundings.add(Offset(xx.toFloat(), yy.toFloat()))
                            }

                        }
                    }


                    drawPoints(obstructions, PointMode.Points, Color.Red, strokeWidth = 5f)
                    drawPoints(surroundings, PointMode.Points, Color.Black, strokeWidth = 5f)



            })

        }
    }
}

【讨论】:

    【解决方案2】:

    我不认为 Compose Canvas 类的当前状态旨在处理这种情况。您必须在组成画布之前绘制所有内容,如果有任何更改,您需要重新绘制所有内容。这适用于轻量级绘图,但不适用于显示 36,000 点。

    一种解决方案是使用较旧的基于视图的画布并缓存绘制的图像,并在使用新数据对其进行更新时重复使用此缓存的图像。您可以使用 Flow 将点按顺序发送到绘图画布,而不是遍历 36,000 个项目。当新点到达时,您将它们输入流中,当它们被收集时,它们被绘制到画布上。但是在绘制新的角度之前,您还需要删除画布上给定角度的最后一个点。要删除前一个,您需要从地图中读取它的最后一个值。通过使用流,您只需在新数据点到达时更新画布。

    即使可组合(托管您的画布)重新组合,您也应该能够检索缓存的画布图像并使用它,而不必通过遍历地图中的所有点来重建整个图像。您只需要确保在视图模型中放置对画布的引用,这样它就不会在可组合组件被重新组合时被破坏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-07-08
      • 1970-01-01
      • 2015-07-23
      • 2021-11-01
      • 1970-01-01
      • 2010-12-30
      • 2021-07-18
      • 2014-02-26
      相关资源
      最近更新 更多