为什么我的 tornadoFX ObservableList 没有收到更新?

Why does my tornadoFX ObservableList not receive updates?

我有一个简单的 tornadoFX 程序,可以在屏幕上的随机位置生成一些圆圈。但是,绘制了 none 个圆圈。我添加了一些调试代码来在绘制圆时打印一条线,它只打印一次。

我希望圆圈以 100 毫秒的间隔出现,当我单击 "Add actor" 按钮时也是如此。

private const val WINDOW_HEIGHT = 600
private const val WINDOW_WIDTH = 1024

fun main(args: Array<String>) {
    Application.launch(MainApp::class.java, *args)
}

class MainApp : App(WorldView::class, Stylesheet::class)

data class Actor(val x: Double, val y: Double)

class WorldView: View("Actor Simulator") {
    override val root = VBox()
    private val actors = ArrayList<Actor>(0)

    init {
        tornadofx.runAsync {
            (0..100).forEach {
                val x = ThreadLocalRandom.current().nextDouble(0.0, WINDOW_WIDTH.toDouble())
                val y = ThreadLocalRandom.current().nextDouble(0.0, WINDOW_HEIGHT.toDouble())
                actors.add(Actor(x, y))
                Thread.sleep(100)
            }
        }
    }

    init {
        with(root) {
            stackpane {
                group {
                    bindChildren(actors.observable()) {
                        circle {
                            centerX = it.x
                            centerY = it.y
                            radius = 10.0
                            also {
                                println("drew circle")
                            }
                        }
                    }
                }
                button("Add actor") {
                    action {
                        actors.add(Actor(0.0, 0.0))
                    }
                }
            }
        }
    }
}

奇怪的是,如果我在画圆的代码中放置一个断点,就会画出圆并打印调试行。

一些观察:

  1. 调用 someList.observable() 将创建一个由底层列表支持的可观察列表,但底层列表上的突变不会发出事件。您应该立即将 actors 初始化为可观察列表。

  2. 对可观察列表的访问必须发生在UI线程上,因此您需要在runLater.

  3. 中包装突变调用
  4. 对于尝试 运行 你的例子的人 - 你没有包含样式表,但在你的 App subclass 中引用了一个样式表,所以 IDEA很可能会导入 TornadoFX Stylesheet class。这不会有好结果:)

  5. also调用没有效果,所以我去掉了

  6. 我将您的代码更新为最佳实践,例如关于如何创建根节点:)

考虑到这些要点的更新示例如下所示:

private const val WINDOW_HEIGHT = 600.0
private const val WINDOW_WIDTH = 1024.0

class MainApp : App(WorldView::class)

data class Actor(val x: Double, val y: Double)

class WorldView : View("Actor Simulator") {

    private val actors = FXCollections.observableArrayList<Actor>()

    override fun onDock() {
        runAsync {
            (0..100).forEach {
                val x = ThreadLocalRandom.current().nextDouble(0.0, WINDOW_WIDTH.toDouble())
                val y = ThreadLocalRandom.current().nextDouble(0.0, WINDOW_HEIGHT.toDouble())
                runLater {
                    actors.add(Actor(x, y))
                }
                Thread.sleep(100)
            }
        }
    }

    override val root = stackpane {
        setPrefSize(WINDOW_WIDTH, WINDOW_HEIGHT)
        group {
            bindChildren(actors) {
                circle {
                    centerX = it.x
                    centerY = it.y
                    radius = 10.0
                    println("drew circle")
                }
            }
        }
        button("Add actor") {
            action {
                actors.add(Actor(0.0, 0.0))
            }
        }
    }
}