Tornadofx – 如何将参数传递给每一个实例的片段

我是javafx,kotlin和显然tornadofx的新手。
问题
如何将参数传递给每一个实例的片段?

比方说,我有一个表视图布局作为我的片段。 现在这个片段被用在多个地方,但有不同的数据集。

例如。 在下面添加片段:

class SomeView : View() { ... root += SomeViewFragment::class } class SomeAnotherView : View() { ... root += SomeViewFragment::class } 

声明片段:

 class SomeViewFragment : Fragment() { ... tableview(someDataSetFromRestApiCall) { ... } } 

如何从SomeView和SomeAnotherView传递不同的someDataSetFromRestApiCall?

让我们以最明确的方式将数据传递给碎片。 对于这个TableView示例,您可以在Fragment中公开一个可观察列表,并将您的TableView绑定到该列表。 然后,您可以从Fragment外部更新该列表,并将您的更改反映在片段中。 对于这个例子,我用一个名为SomeItem的可观察属性创建了一个简单的数据对象:

 class SomeItem(name: String) { val nameProperty = SimpleStringProperty(name) var name by nameProperty } 

现在我们可以定义一个SomeViewFragment属性绑定到TableView的SomeViewFragment

 class SomeViewFragment : Fragment() { val items = FXCollections.observableArrayList<SomeItem>() override val root = tableview(items) { column("Name", SomeItem::nameProperty) } } 

如果稍后更新项目内容,则更改将反映在表格中:

 class SomeView : View() { override val root = stackpane { this += find<SomeViewFragment>().apply { items.setAll(SomeItem("Item A"), SomeItem("Item B")) } } } 

然后,您可以对SomeOtherView执行相同的操作,但使用其他数据:

 class SomeOtherView : View() { override val root = stackpane { this += find<SomeViewFragment>().apply { items.setAll(SomeItem("Item B"), SomeItem("Item C")) } } } 

虽然这很容易理解并且非常明确,但它会在您的组件之间创建非常强大的耦合。 您可能需要考虑使用范围来代替。 我们现在有两个选择:

  1. 在范围内使用注射
  2. 让范围包含数据

在范围内使用注射

我们将首先使用选项1,通过注入数据模型。 我们首先创建一个可以保存我们物品清单的数据模型:

 class ItemsModel(val items: ObservableList<SomeItem>) : ViewModel() 

现在我们将这个ItemsModel注入到我们的片段中,并从该模型中提取项目:

 class SomeViewFragment : Fragment() { val model: ItemsModel by inject() override val root = tableview(model.items) { column("Name", SomeItem::nameProperty) } } 

最后,我们需要为每个视图中的片段定义一个单独的范围,并为该范围准备数据:

 class SomeView : View() { override val root = stackpane { // Create the model and fill it with data val model= ItemsModel(listOf(SomeItem("Item A"), SomeItem("Item B")).observable()) // Define a new scope and put the model into the scope val fragmentScope = Scope() setInScope(model, fragmentScope) // Add the fragment for our created scope this += find<SomeViewFragment>(fragmentScope) } } 

请不要在setInScope使用的setInScope函数在TornadoFX 1.5.9中可用。 同时您可以使用:

 FX.getComponents(fragmentScope).put(ItemsModel::class, model) 

让范围包含数据

另一个选择是将数据直接放入范围。 我们来创建一个ItemsScope

 class ItemsScope(val items: ObservableList<SomeItem>) : Scope() 

现在我们的片段将期望得到一个SomeItemScope的实例,所以我们投它并提取数据:

 class SomeViewFragment : Fragment() { override val scope = super.scope as ItemsScope override val root = tableview(scope.items) { column("Name", SomeItem::nameProperty) } } 

由于我们不需要这个模型,所以视图需要做更少的工作:

 class SomeView : View() { override val root = stackpane { // Create the scope and fill it with data val itemsScope= ItemsScope(listOf(SomeItem("Item A"), SomeItem("Item B")).observable()) // Add the fragment for our created scope this += find<SomeViewFragment>(itemsScope) } } 

传递参数

编辑 :由于这个问题,我们决定包括支持传递参数与findinject 。 从TornadoFX 1.5.9,你可以发送项目列表作为这样的参数:

 class SomeView : View() { override val root = stackpane { val params = "items" to listOf(SomeItem("Item A"), SomeItem("Item B")).observable() this += find<SomeViewFragment>(params) } } 

SomeViewFragment现在可以拿起这些参数并直接使用它们:

 class SomeViewFragment : Fragment() { val items: ObservableList<SomeItem> by param() override val root = tableview(items) { column("Name", SomeItem::nameProperty) } } 

请不要说,这涉及片段内未经检查的演员。

其他选项

你也可以通过EventBus传递参数和数据,这也将在即将发布的TornadoFX 1.5.9中。 EventBus还支持可以轻松定位事件的范围。

进一步阅读

您可以在指南中阅读更多关于Scopes,EventBus和ViewModel的信息:

领域

EventBus

ViewModel和验证