Создание моего собственного класса кнопки - PullRequest
0 голосов
/ 18 мая 2018

Я использую инфраструктуру TornadoFX и Kotlin для создания простого настольного приложения, которое позволяет пользователю нажимать кнопку, выбирать файл USFM и выплевывать содержимое этого файла USFM в текстовую область.У меня есть основы работы, но я хочу ускорить мой код, отделив логику для моей кнопки.В настоящее время мое единственное представление определяет логику действий, выполняемых при нажатии на мою кнопку.Код выглядит следующим образом:

import tornadofx.*
import javafx.scene.layout.BorderPane
import javafx.stage.FileChooser
import javafx.scene.control.Button
import javafx.scene.control.Label
import javafx.scene.control.TextArea
import java.io.File

class AppView : View() {
    override val root: BorderPane by fxml()

    val chooseFile: Button by fxid<Button>()
    val fileName: Label by fxid<Label>()
    val textArea: TextArea by fxid<TextArea>()
    lateinit var fileChosen: File
    init {
        title = "USFM Viewer"
        root.lookup(".button").setOnMouseClicked {
            var fileFilter = arrayOf(FileChooser.ExtensionFilter("USFM files", "*.usfm"))
            fileChosen = chooseFile("Select a USFM File", fileFilter, FileChooserMode.Single)[0]
            fileName.text = fileChosen.name
            val fileParser = USFMFileParser(fileChosen)
            val printout = fileParser.readFile(fileChosen)
            textArea.text = printout.joinToString()
        }

    }
}

Мой первоначальный план состоит в том, чтобы создать свой собственный класс кнопок, который наследуется от класса Button и либо переопределяет, либо инициализирует своим собственным методом для обработки событий нажатия.Цель состоит в том, чтобы просто объявить кнопку в моем файле представления и не определять для нее дополнительную логику.Я, кажется, изо всех сил в получении этой работы

Это мой прототип для моего пользовательского класса кнопок:

import javafx.scene.control.Button
import javafx.stage.FileChooser
import tornadofx.*
import java.io.File

class ChooseFileButton: Button(){
    val fileFilter = arrayOf(FileChooser.ExtensionFilter("USFM File", "*.usfm"))
    val files: List<File>
        get() = chooseFile("Select a USFM File", fileFilter, FileChooserMode.Single)
    init {
        action { println(this.files[0].name) }
    }
}

Кроме того, вот мой файл FXML, чтобы предоставить больше контекста для элементов на моей стадии:

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.BorderPane?>

<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.FlowPane?>
<BorderPane id="appview"
            xmlns="http://javafx.com/javafx/8.0.40"
            xmlns:fx="http://javafx.com/fxml/1"
            prefHeight="635"
            prefWidth="500">
    <top>
        <FlowPane>
            <Button fx:id="chooseFile" id="chooseFile" text="Choose File..." />
            <Label fx:id="fileName" id="fileName" text="No File Chosen"/>
        </FlowPane>
    </top>
    <center>
        <ScrollPane>
            <TextArea fx:id="textArea" id="textArea"></TextArea>
        </ScrollPane>
    </center>
</BorderPane>

Есть ли у кого-нибудь какие-либо советы, рекомендации, ссылки и т. Д., Как мне отделить логику кнопок от вида?Это вообще возможно?

1 Ответ

0 голосов
/ 19 мая 2018

Для этого не нужно создавать подкласс Button.Просто создайте функцию расширения на ButtonBase, которая настраивает кнопку так, как вы хотите, и вызывайте эту функцию один раз для каждой кнопки.Вам также следует рассмотреть возможность использования FXML для использования конструкторов безопасных типов, что сделало бы это еще более удобным, не говоря уже о безопасных типах.

Вот пример такой функции, которая позволяет указывать парсер, фильтрыи загрузите файл в целевое строковое свойство.Существует также парсер по умолчанию, который просто загружает файл как есть.

fun ButtonBase.loadFile(target: Property<String>,
                        parser: (File) -> String = { Files.readAllBytes(it.toPath()).toString(StandardCharsets.UTF_8) },
                        filters: Array<FileChooser.ExtensionFilter>) {
    action {
        chooseFile(text, filters, FileChooserMode.Single).firstOrNull()?.let {
            target.value = parser.invoke(it)
        }
    }
}

Вот полный пример безопасного конструктора типов с использованием функции:

class FileChooserTest : View() {
    val fileContent = SimpleStringProperty()

    override val root = borderpane {
        center {
            textarea(fileContent)
        }
        bottom {
            buttonbar {
                button("Select a USFM File") {
                    loadFile(fileContent, filters = arrayOf(FileChooser.ExtensionFilter("USFM File", "*.usfm")))
                }
            }
        }
    }
}

Если вы действительно хотите придерживатьсяв FXML (может быть, вам просто нравится набирать вещи :), вы бы назвали это так:

class AppView : View("USFM Viewer") {
    override val root: BorderPane by fxml()

    val chooseFile: Button by fxid<Button>()
    val textArea: TextArea by fxid<TextArea>()

    init {
        chooseFile.loadFile(textArea.textProperty(), filters = arrayOf(FileChooser.ExtensionFilter("USFM File", "*.usfm")))
    }
}

Функция loadFile может быть расширена любым удобным вам способом, например, путем возврата выбранного файла,назначение отдельного заголовка диалоговому окну выбора файлов и т. д.

Еще один совет: в своем примере кода вы нашли кнопку с помощью делегата by fxml(), но затем вы сделали еще один lookup, чтобы снова найти кнопку.Вы должны повторно использовать кнопку, которую вы уже нашли.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...