如何重构视图以允许过滤绑定到 tornadofx 应用程序内的 observableArrayList 的项目
How do I refactor a view to allow filtering of items bound to an observableArrayList inside a tornadofx app
我从 gradle hello-world
示例开始 https://github.com:JetBrains/kotlin-examples.git 并将其修改为使用 TornadoFX。
这是一个显示项目列表的应用程序。您可以添加到列表中,RequestView
将自动显示所有项目。
我让它工作,以便存储的项目绑定到 observableArrayList
,但我现在想使用底部的 TextView
实现过滤器。但是,我很难理解这是否意味着我应该创建一个在 RequestView
内部管理的新列表,并从中进行过滤,或者如何操作。
package demo
import javafx.collections.FXCollections
import javafx.geometry.Pos
import javafx.scene.control.TextField
import javafx.scene.layout.VBox
import javafx.scene.text.FontWeight
import tornadofx.*
class helloWorldApp : App(HelloWorld::class) {
}
class HelloWorld : View() {
override val root = VBox()
var requestView: RequestView by singleAssign()
var filterField: TextField by singleAssign()
init {
with(root) {
requestView = RequestView()
this += requestView
filterField = TextField()
this += filterField
}
requestView.items.add("Hi there")
requestView.items.add("Another one")
}
}
class RequestView() : View() {
var items = FXCollections.observableArrayList<String>()
override val root = listview(items) {
cellFormat {
graphic = cache {
form {
fieldset {
label(it) {
alignment = Pos.CENTER_LEFT
style {
fontSize = 15.px
fontWeight = FontWeight.BOLD
}
}
}
}
}
}
}
}
这是 build.gradle 文件,以备不时之需。
buildscript {
ext.kotlin_version = '1.1.2'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = 'demo.helloWorldApp'
defaultTasks 'run'
repositories {
mavenCentral()
}
tasks.compileKotlin.kotlinOptions.jvmTarget = "1.8"
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testCompile 'junit:junit:4.11'
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
compile 'no.tornado:tornadofx:1.7.10'
}
task wrapper(type: Wrapper) {
gradleVersion = "2.7"
}
您应该使用包装 ObservableList 并接受用于区分条目的谓词的 SortedFilteredList
。
不幸的是,您的两个视图之间存在耦合,因此您应该考虑改为触发一个事件,但这里有一个对您的示例进行最少更改的可行解决方案。我确实将数据移动到模型中,并清理了 ui 代码,并删除了 singleAssign
语句并将一些最佳实践应用于 builders :)
如您所见,SortedFilteredList
有一个 filterWhen
函数,只要文本字段的 textProperty()
发生变化,就会调用该函数。
class HelloWorldApp : App(HelloWorld::class)
class HelloWorld : View() {
val requestView: RequestView by inject()
override val root = vbox {
add(requestView)
textfield {
promptText = "Filter"
requestView.data.filterWhen(textProperty()) { query, item ->
item.contains(query, ignoreCase = true)
}
}
}
}
class ItemsModel : ViewModel() {
val items = FXCollections.observableArrayList<String>()
fun addItem(item: String) = items.add(item)
init {
addItem("Hi there")
addItem("Another one")
}
}
class RequestView() : View() {
val model: ItemsModel by inject()
val data = SortedFilteredList(model.items)
override val root = listview(data) {
cellFormat {
graphic = cache {
form {
fieldset {
label(it) {
alignment = Pos.CENTER_LEFT
style {
fontSize = 15.px
fontWeight = FontWeight.BOLD
}
}
}
}
}
}
}
}
我从 gradle hello-world
示例开始 https://github.com:JetBrains/kotlin-examples.git 并将其修改为使用 TornadoFX。
这是一个显示项目列表的应用程序。您可以添加到列表中,RequestView
将自动显示所有项目。
我让它工作,以便存储的项目绑定到 observableArrayList
,但我现在想使用底部的 TextView
实现过滤器。但是,我很难理解这是否意味着我应该创建一个在 RequestView
内部管理的新列表,并从中进行过滤,或者如何操作。
package demo
import javafx.collections.FXCollections
import javafx.geometry.Pos
import javafx.scene.control.TextField
import javafx.scene.layout.VBox
import javafx.scene.text.FontWeight
import tornadofx.*
class helloWorldApp : App(HelloWorld::class) {
}
class HelloWorld : View() {
override val root = VBox()
var requestView: RequestView by singleAssign()
var filterField: TextField by singleAssign()
init {
with(root) {
requestView = RequestView()
this += requestView
filterField = TextField()
this += filterField
}
requestView.items.add("Hi there")
requestView.items.add("Another one")
}
}
class RequestView() : View() {
var items = FXCollections.observableArrayList<String>()
override val root = listview(items) {
cellFormat {
graphic = cache {
form {
fieldset {
label(it) {
alignment = Pos.CENTER_LEFT
style {
fontSize = 15.px
fontWeight = FontWeight.BOLD
}
}
}
}
}
}
}
}
这是 build.gradle 文件,以备不时之需。
buildscript {
ext.kotlin_version = '1.1.2'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = 'demo.helloWorldApp'
defaultTasks 'run'
repositories {
mavenCentral()
}
tasks.compileKotlin.kotlinOptions.jvmTarget = "1.8"
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testCompile 'junit:junit:4.11'
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
compile 'no.tornado:tornadofx:1.7.10'
}
task wrapper(type: Wrapper) {
gradleVersion = "2.7"
}
您应该使用包装 ObservableList 并接受用于区分条目的谓词的 SortedFilteredList
。
不幸的是,您的两个视图之间存在耦合,因此您应该考虑改为触发一个事件,但这里有一个对您的示例进行最少更改的可行解决方案。我确实将数据移动到模型中,并清理了 ui 代码,并删除了 singleAssign
语句并将一些最佳实践应用于 builders :)
如您所见,SortedFilteredList
有一个 filterWhen
函数,只要文本字段的 textProperty()
发生变化,就会调用该函数。
class HelloWorldApp : App(HelloWorld::class)
class HelloWorld : View() {
val requestView: RequestView by inject()
override val root = vbox {
add(requestView)
textfield {
promptText = "Filter"
requestView.data.filterWhen(textProperty()) { query, item ->
item.contains(query, ignoreCase = true)
}
}
}
}
class ItemsModel : ViewModel() {
val items = FXCollections.observableArrayList<String>()
fun addItem(item: String) = items.add(item)
init {
addItem("Hi there")
addItem("Another one")
}
}
class RequestView() : View() {
val model: ItemsModel by inject()
val data = SortedFilteredList(model.items)
override val root = listview(data) {
cellFormat {
graphic = cache {
form {
fieldset {
label(it) {
alignment = Pos.CENTER_LEFT
style {
fontSize = 15.px
fontWeight = FontWeight.BOLD
}
}
}
}
}
}
}
}