TreeView 的异步加载
Async Loading of a TreeView
嘿,我是 tornadofx 的新手,正在努力为树视图异步加载数据。我正在从休息端点加载类别,我想在其中显示。
似乎没有直接绑定到子项的数据。
当使用 'bindChildren' 时,我可以提供可观察列表,但我必须将它们转换为 Node 的。这将使填充块有点过时。
推荐的方法是什么?我找不到任何相关信息。
// Category
interface Category<T : Category<T>> {
val id: String
val name: String
val subcategories: List<T>?
}
//default category:
class DefaultCategory(override val name: String) : Category<DefaultCategory> {
override val id: String = "default"
override val subcategories: List<DefaultCategory>? = null
}
//ViewModel
class CategoryViewModel : ViewModel() {
val sourceProperty = SimpleListProperty<Category<*>>()
fun loadData() {
// load items for treeview into 'newItems'
sourceProperty.value = newItems
}
}
// TreeViewFactoryMethod
private fun createTreeView(
listProperty: SimpleListProperty<Category<*>>
): TreeView<Category<*>> {
return treeview {
root = TreeItem(DefaultCategory("Categories"))
isShowRoot = false
root.isExpanded = true
root.children.forEach { it.isExpanded = true }
cellFormat { text = it.name }
populate { parent ->
when (parent) {
root -> listProperty.value
else -> parent.value.subcategories
}
}
}
}
假设在单击按钮时我调用 viewmodel.loadData()
,我希望 TreeView 在有一些新数据时立即更新。 (如果我能找到绑定方法的话)
我以前从来没有为 TornadoFX 使用过 bindChildren
,你对异步的使用与我认为你的主要问题不是很相关。所以,不可否认,这个问题起初让我有些困惑,但我猜你只是想知道为什么列表没有出现在你的 TreeView
中?我做了一个测试示例,并进行了更改以使其正常工作。
// Category
interface Category<T : Category<T>> {
val id: String
val name: String
val subcategories: List<T>?
}
//default category:
class DefaultCategory(override val name: String) : Category<DefaultCategory> {
override val id: String = "default"
override val subcategories: List<DefaultCategory>? = null
}
//Just a dummy category
class ChildCategory(override val name: String) : Category<ChildCategory> {
override val id = name
override val subcategories: List<ChildCategory>? = null
}
//ViewModel
class CategoryViewModel : ViewModel() {
//filled with dummy data
val sourceProperty = SimpleListProperty<Category<*>>(listOf(
ChildCategory("Categorya"),
ChildCategory("Categoryb"),
ChildCategory("Categoryc"),
ChildCategory("Categoryd")
).asObservable())
fun loadData() {
sourceProperty.asyncItems {
//items grabbed somehow
listOf(
ChildCategory("Category1"),
ChildCategory("Category2"),
ChildCategory("Category3"),
ChildCategory("Category4")
).asObservable()
}
}
}
class TestView : View() {
val model: CategoryViewModel by inject()
override val root = vbox(10) {
button("Refresh Items").action {
model.loadData()
}
add(createTreeView(model.sourceProperty))
}
// TreeViewFactoryMethod
private fun createTreeView(
listProperty: SimpleListProperty<Category<*>>
): TreeView<Category<*>> {
return treeview {
root = TreeItem(DefaultCategory("Categories"))
isShowRoot = false
root.isExpanded = true
root.children.forEach { it.isExpanded = true }
cellFormat { text = it.name }
populate { parent ->
when (parent) {
root -> listProperty
else -> parent.value.subcategories
}
}
}
}
}
有两个重要的区别。
1. 更相关的区别是在填充块内部,使用 root -> listProperty
而不是 root.listProperty.value
。这将使您的列表出现。原因是 SimpleListProperty
不是列表,它 包含 列表。所以,是的,传入一个普通列表是完全有效的(就像你传入列表 属性 的 value 的方式一样)。但现在这意味着树视图不听你的 属性,只听你传入的列表。考虑到这一点,我会体谅类别的子类别列表也已实现。
2.其次,注意ViewModel
中asyncItems
的使用。这将异步执行任何任务,然后将项目设置为成功列出。您甚至可以向其中添加 fail
或 cancel
块。我建议使用它,因为 long/intensive 操作不应在 UI 线程上执行。
嘿,我是 tornadofx 的新手,正在努力为树视图异步加载数据。我正在从休息端点加载类别,我想在其中显示。
似乎没有直接绑定到子项的数据。
当使用 'bindChildren' 时,我可以提供可观察列表,但我必须将它们转换为 Node 的。这将使填充块有点过时。
推荐的方法是什么?我找不到任何相关信息。
// Category
interface Category<T : Category<T>> {
val id: String
val name: String
val subcategories: List<T>?
}
//default category:
class DefaultCategory(override val name: String) : Category<DefaultCategory> {
override val id: String = "default"
override val subcategories: List<DefaultCategory>? = null
}
//ViewModel
class CategoryViewModel : ViewModel() {
val sourceProperty = SimpleListProperty<Category<*>>()
fun loadData() {
// load items for treeview into 'newItems'
sourceProperty.value = newItems
}
}
// TreeViewFactoryMethod
private fun createTreeView(
listProperty: SimpleListProperty<Category<*>>
): TreeView<Category<*>> {
return treeview {
root = TreeItem(DefaultCategory("Categories"))
isShowRoot = false
root.isExpanded = true
root.children.forEach { it.isExpanded = true }
cellFormat { text = it.name }
populate { parent ->
when (parent) {
root -> listProperty.value
else -> parent.value.subcategories
}
}
}
}
假设在单击按钮时我调用 viewmodel.loadData()
,我希望 TreeView 在有一些新数据时立即更新。 (如果我能找到绑定方法的话)
我以前从来没有为 TornadoFX 使用过 bindChildren
,你对异步的使用与我认为你的主要问题不是很相关。所以,不可否认,这个问题起初让我有些困惑,但我猜你只是想知道为什么列表没有出现在你的 TreeView
中?我做了一个测试示例,并进行了更改以使其正常工作。
// Category
interface Category<T : Category<T>> {
val id: String
val name: String
val subcategories: List<T>?
}
//default category:
class DefaultCategory(override val name: String) : Category<DefaultCategory> {
override val id: String = "default"
override val subcategories: List<DefaultCategory>? = null
}
//Just a dummy category
class ChildCategory(override val name: String) : Category<ChildCategory> {
override val id = name
override val subcategories: List<ChildCategory>? = null
}
//ViewModel
class CategoryViewModel : ViewModel() {
//filled with dummy data
val sourceProperty = SimpleListProperty<Category<*>>(listOf(
ChildCategory("Categorya"),
ChildCategory("Categoryb"),
ChildCategory("Categoryc"),
ChildCategory("Categoryd")
).asObservable())
fun loadData() {
sourceProperty.asyncItems {
//items grabbed somehow
listOf(
ChildCategory("Category1"),
ChildCategory("Category2"),
ChildCategory("Category3"),
ChildCategory("Category4")
).asObservable()
}
}
}
class TestView : View() {
val model: CategoryViewModel by inject()
override val root = vbox(10) {
button("Refresh Items").action {
model.loadData()
}
add(createTreeView(model.sourceProperty))
}
// TreeViewFactoryMethod
private fun createTreeView(
listProperty: SimpleListProperty<Category<*>>
): TreeView<Category<*>> {
return treeview {
root = TreeItem(DefaultCategory("Categories"))
isShowRoot = false
root.isExpanded = true
root.children.forEach { it.isExpanded = true }
cellFormat { text = it.name }
populate { parent ->
when (parent) {
root -> listProperty
else -> parent.value.subcategories
}
}
}
}
}
有两个重要的区别。
1. 更相关的区别是在填充块内部,使用 root -> listProperty
而不是 root.listProperty.value
。这将使您的列表出现。原因是 SimpleListProperty
不是列表,它 包含 列表。所以,是的,传入一个普通列表是完全有效的(就像你传入列表 属性 的 value 的方式一样)。但现在这意味着树视图不听你的 属性,只听你传入的列表。考虑到这一点,我会体谅类别的子类别列表也已实现。
2.其次,注意ViewModel
中asyncItems
的使用。这将异步执行任何任务,然后将项目设置为成功列出。您甚至可以向其中添加 fail
或 cancel
块。我建议使用它,因为 long/intensive 操作不应在 UI 线程上执行。