TornadoFX JavaFX Sync跨桌面浏览

我正在尝试在桌面浏览器中同步滚动条。 (水平和垂直)

在这里输入图像描述

SyncScrollEx视图有两个tableView,基本上是一个并排放置的片段,具有相同的数据集,因此具有相同的表格大小布局。

预期的行为:当我在一个tableview上滚动时,另一个tableview的滚动条也应该滚动相同的数量。

以下是我目前的进展:

import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleStringProperty import javafx.collections.FXCollections import javafx.scene.control.ScrollBar import tornadofx.* class SyncScrollEx : View() { override val root = hbox { setPrefSize(300.0, 150.0) this += find<MyTableFrag>() this += find<MyTableFrag>() } } class MyTableFrag : Fragment() { var addEventOnlyOnceFlag = false val persons = FXCollections.observableArrayList<GameWarrior>( GameWarrior(1,"Tyrion Lannister", "M"), GameWarrior(2,"Ned Stark", "M"), GameWarrior(3,"Sansa Stark", "F"), GameWarrior(4,"Daenerys Targaryen", "F"), GameWarrior(5,"Bran Stark", "M"), GameWarrior(6,"Jon Snow", "M"), GameWarrior(7,"Arya Stark", "F") ) override val root = vbox { tableview(persons) { column("ID", GameWarrior::idProperty) column("Name", GameWarrior::nameProperty) column("Gender", GameWarrior::genderProperty) subscribe<SyncScrollEvent> { event -> //Sync the ScrollX & ScrollY of both the tables event.node.value = event.newVal.toDouble() } //Hack, need to initialize this when the table/scroll is rendered setOnMouseEntered { //Hack for not triggering the lookupAll event on every mouse enter if (!addEventOnlyOnceFlag) { addEventOnlyOnceFlag = true //INFO: Look up for the scroll bars in tableView and add a listener this.lookupAll(".scroll-bar").map { node -> if (node is ScrollBar) { node.valueProperty().addListener { value, oldValue, newValue -> println(node.orientation.toString() + " " + newValue) fire(SyncScrollEvent(node, newValue)) } } } } } } } } class GameWarrior(id: Int, name: String, gender: String) { val idProperty = SimpleIntegerProperty(id) var id by idProperty val nameProperty = SimpleStringProperty(name) var name by nameProperty val genderProperty = SimpleStringProperty(gender) var gender by genderProperty } class SyncScrollEvent(val node: ScrollBar, val newVal: Number) : FXEvent() 

评论突出了我面临的问题。
此外,我不明白如何在这种情况下调用“订阅”两个表视图Fire()发生在一个EventListener

首先,我们需要干净的访问滚动条。 当TableView被分配它的皮肤时,滚动条将可用。 我们将创建一个关于方向的地图来跟踪它们:

 val scrollbars = HashMap<Orientation, ScrollBar>() 

一旦皮肤可用,我们查找滚动条,并将其分配给我们的地图,并听取更改,以便我们可以触发事件

 skinProperty().onChange { this.lookupAll(".scroll-bar").map { it as ScrollBar }.forEach { bar -> scrollbars[bar.orientation] = bar bar.valueProperty().onChange { fire(SyncScrollEvent(bar, this)) } } } 

我们不需要事件中的位置,因为我们可以查询滚动条的值,但是如果添加源TableView,则更容易过滤事件。 SyncScrollEvent现在看起来像这样:

 class SyncScrollEvent(val scrollbar: ScrollBar, val table: TableView<*>) : FXEvent() 

让我们来监听滚动事件,并确保只有当事件来自另一个tableview时,我们才能更改滚动条的值。

 subscribe<SyncScrollEvent> { event -> if (event.table != this) scrollbars[event.scrollbar.orientation]?.value = event.scrollbar.value } 

为了完整性,这里是整个修改后的应用程序:

 import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleStringProperty import javafx.collections.FXCollections import javafx.geometry.Orientation import javafx.scene.control.ScrollBar import javafx.scene.control.TableView import tornadofx.* import java.util.* class SyncScrollEx : View() { override val root = hbox { setPrefSize(300.0, 150.0) add(MyTableFrag::class) add(MyTableFrag::class) } } class MyTableFrag : Fragment() { val persons = FXCollections.observableArrayList<GameWarrior>( GameWarrior(1, "Tyrion Lannister", "M"), GameWarrior(2, "Ned Stark", "M"), GameWarrior(3, "Sansa Stark", "F"), GameWarrior(4, "Daenerys Targaryen", "F"), GameWarrior(5, "Bran Stark", "M"), GameWarrior(6, "Jon Snow", "M"), GameWarrior(7, "Arya Stark", "F") ) val scrollbars = HashMap<Orientation, ScrollBar>() override val root = vbox { tableview(persons) { column("ID", GameWarrior::idProperty) column("Name", GameWarrior::nameProperty) column("Gender", GameWarrior::genderProperty) subscribe<SyncScrollEvent> { event -> if (event.table != this) scrollbars[event.scrollbar.orientation]?.value = event.scrollbar.value } skinProperty().onChange { this.lookupAll(".scroll-bar").map { it as ScrollBar }.forEach { bar -> scrollbars[bar.orientation] = bar bar.valueProperty().onChange { fire(SyncScrollEvent(bar, this)) } } } } } } class GameWarrior(id: Int, name: String, gender: String) { val idProperty = SimpleIntegerProperty(id) var id by idProperty val nameProperty = SimpleStringProperty(name) var name by nameProperty val genderProperty = SimpleStringProperty(gender) var gender by genderProperty } class SyncScrollEvent(val scrollbar: ScrollBar, val table: TableView<*>) : FXEvent() 
Interesting Posts