本文介绍了TornadoFX JavaFX Sync滚动浏览表格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在tableviews上同步滚动。 (水平和垂直)

I am trying to synchronise scrolls across the tableviews. (Both Horizontal & Vertical)

SyncScrollEx View有两个tableView,它基本上是一个并排放置的Fragment,具有相同的数据集,因此具有相同的表大小布局。

The SyncScrollEx View has two tableView which is basically one Fragment placed side by side, with same dataset, and hence same table size layout.

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

Expected Behaviour: When I scroll on one tableview, the other tableview's scrollbar should also scroll for the same amount.

以下是我目前的进展:

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()

评论突出了我所面临的问题。

另外,我无法理解如何为两个tableviews调用subscribe在这种情况下,Fire()发生在Ev内部entListener

The comments highlight the problems I am facing.
Also, I fail to understand how the "subscribe" will get invoked for both the tableviews in such scenario where Fire() happens inside a EventListener

推荐答案

首先,我们需要对滚动条进行干净访问。当为TableView分配它的皮肤时,滚动条将可用。我们将创建一个键入方向的地图以跟踪它们:

First we need clean access to the scrollbars. When the TableView is assigned it's skin, the scrollbars will be available. We'll create a map keyed on orientation to keep track of them:

val scrollbars = HashMap<Orientation, ScrollBar>()

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

Once the skin is available we look up the scrollbars and assign them to our map and listen for changes so we can fire the event

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现在看起来像这样:

We don't need the position in the event, since we can query the scrollbar for it's value, but it's easier to filter out the events if we add the source TableView. The SyncScrollEvent now looks like this:

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

让我们听一下滚动事件并确保我们只在事件发生时更改我们的滚动条值从其他tableview,对应的方向:

Let's listen for the scroll events and make sure we only change our scrollbar value if the event originates from the other tableview, for the corresponding orientation:

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

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

For completeness, here is the whole modified app:

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()

这篇关于TornadoFX JavaFX Sync滚动浏览表格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-01 18:26