В моем настольном приложении Kotlin, использующем TornadoFX, я создал макет AudioCard
(подкласс VBox
), который имеет несколько меток и основные элементы управления аудиопроигрывателем. Это AudioCard
имеет AudioCardViewModel
, который обрабатывает события из пользовательского интерфейса, и AudioCardModel
, который содержит информацию, такую как заголовок, субтитры, путь к аудиофайлу и т. Д. Упрощенная версия показана ниже.
data class AudioCardModel(
var title: String,
var audioFile: File
)
class AudioCardViewModel(title: String, audioFile: File) {
val model = AudioCardModel(title, audioFile)
var titleProperty = SimpleStringProperty(model.title)
fun playButtonPressed() {
// play the audio file from the model
}
}
class AudioCard(title: String, audioFile: File) : VBox() {
val viewModel = AudioCardViewModel(title, audioFile)
init {
// create the UI
label(title) {
bind(viewModel.titleProperty)
}
button("Play") {
viewModel.playButtonPressed()
}
}
}
До этого момента я пытался сохранить код как можно более общим, позволяя себе или другим повторно использовать этот компонент пользовательского интерфейса в будущих приложениях, которые должны воспроизводить звук. Однако для моего текущего приложения наиболее целесообразно иметь более специализированную версию этого компонента пользовательского интерфейса, которая инициализируется непосредственно из моего класса модели данных и может расширять некоторые действия. Я пробовал что-то вроде этого (обязательные поля и классы из предыдущего блока кода были переключены на open
):
data class CustomAudioCardModel(
var customData: CustomData
)
class CustomAudioCardViewModel(customData: CustomData)
: AudioCardViewModel(customData.name, customData.file) {
val model = CustomAudioCardModel(customData)
override fun playButtonPressed() {
super.playButtonPressed()
// do secondary things only needed by CustomAudioCardViewModel
}
}
class CustomAudioCard(customData: CustomData): AudioCard(customData.name, customData.file) {
override val viewModel = CustomAudioCardViewModel(customData)
}
К сожалению, это не так просто. Переопределив viewModel
в CustomAudioCard
, свойство viewModel
перестает быть окончательным, вызывая NullPointerException, когда функция инициализации суперкласса AudioCard
пытается использовать модель представления для установки метки заголовка до того, как дочерний класс имеет инициализировал модель представления.
Я подозреваю, что может быть выход из этого путем определения интерфейса AudioCardViewModel
и / или использования возможности Kotlin делегировать с ключевым словом by
, но у меня сложилось впечатление, что определение интерфейса (как в MVP ) не должен быть необходим для MVVM.
Подводя итог: Как правильно расширить существующий элемент управления MVVM, особенно в контексте библиотеки Kotlin TornadoFX?