首页 > 解决方案 > tornadofx listview 正在创建一个额外的空 listcellfragment,而不是列表中的项目

问题描述

我有一个 ListView 的 ViewModel,里面有 3 个玩家:

object PlayerListViewModel : ViewModel() {
    lateinit var players : ObservableList<Player>

    init{

    }

    fun loadPlayers(){
        players = Engine.selectedGame.players.asObservable()
    }
}

class PlayerListView : View() {
    private val vm = PlayerListViewModel

    override val root = VBox()

    init {
        vm.loadPlayers()

        root.replaceChildren {
            style {
                spacing = 25.px
                alignment = Pos.CENTER
                padding = box(0.px, 15.px)
            }

            listview(vm.players){
                style{
                    background = Background.EMPTY
                    prefWidth = 300.px
                }
                isFocusTraversable = false
                isMouseTransparent = true
                cellFragment(PlayerCardFragment::class)
            }
        }
    }
}

由于某种原因,listview正在创建 4 PlayerCardFragments,第一个具有 nullitem属性,最后 3 个具有正确的Player项目引用。这是PlayerCardFragment定义:

class PlayerCardFragment : ListCellFragment<Player>() {
    private val logger = KotlinLogging.logger { }

    private val vm = PlayerViewModel().bindTo(this)
    private lateinit var nameLabel : Label
    private lateinit var scoreLabel : Label

    override val root = hbox {
        addClass(UIAppStyle.playerCard)
        nameLabel = label(vm.name) { addClass(UIAppStyle.nameLabel) }
        scoreLabel = label(vm.score) { addClass(UIAppStyle.scoreLabel) }
    }

    init {
        logger.debug { "Initializing fragment for ${this.item} and ${vm.name.value}" }
        EventBus.channel(EngineEvent.PlayerChanged::class)
            .observeOnFx()
            .subscribe() {
                vm.rollback() //force viewmodel (PlayerViewModel) refresh since model (Player) does not implement observable properties
                logger.debug { "${vm.name.value}'s turn is ${vm.myTurn.value}" }
                root.toggleClass(UIAppStyle.selected, vm.myTurn)
     }
}

运行应用程序时,PlayerCardFragment初始化打印四次“Initializing fragment for null and null”,但列表显示完美正确,包含 3 个 Player 项目。稍后在执行过程中,Engine.PlayerChanged如果收到一个事件,Oberver 函数会打印:“null's turn is false”“Adam's turn is false”“Chad's turn is true”“Kyle's turn is false”这些是正确的玩家,有正确的回合状态。随着样式的变化,它listview看起来非常好。我只是不确定第一个 null ListCellFragment 来自哪里。

标签: kotlinjavafxtornadofx

解决方案


似乎您正试图ItemViewModel通过改变它周围的一切来为视图模型提供功能。为什么不改成PlayerViewModel拥有这个功能呢?最简单的想象方法是从泛型属性中创建绑定,然后通过监听将它们全部更改并提交itemProperty

class Player(var foo: String?, var bar: Int?)

class PlayerViewModel() : ItemViewModel<Player>() {
    val foo = bind { SimpleStringProperty() }
    val bar = bind { SimpleIntegerProperty() }
    
    init {
        itemProperty.onChange { 
            foo.value = it?.foo
            bar.value = it?.bar
        }
    }

    override fun onCommit() {
        item?.let { player ->
            player.foo = foo.value
            player.bar = bar.value?.toInt()
        }
    }
}

漂亮吗?不。它是否使您不必实施事件系统?是的。


推荐阅读