制作我自己的按钮 Class
Making My Own Button Class
我正在使用 TornadoFX 框架和 Kotlin 创建一个简单的桌面应用程序,让用户单击一个按钮,select 一个 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()
}
}
}
我最初的计划是创建我自己的按钮 class,该按钮继承自按钮 class,并使用自己的方法覆盖或初始化处理点击事件。目标是简单地在我的视图文件中声明按钮,而不必为其定义额外的逻辑。我似乎在努力完成这项工作。
这是我的自定义按钮原型 class:
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>
有没有人有关于如何将按钮逻辑与视图分开的提示、建议、链接等?这可能吗?
无需为此创建 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
来再次定位按钮。您应该重新使用您已经找到的按钮。
我正在使用 TornadoFX 框架和 Kotlin 创建一个简单的桌面应用程序,让用户单击一个按钮,select 一个 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()
}
}
}
我最初的计划是创建我自己的按钮 class,该按钮继承自按钮 class,并使用自己的方法覆盖或初始化处理点击事件。目标是简单地在我的视图文件中声明按钮,而不必为其定义额外的逻辑。我似乎在努力完成这项工作。
这是我的自定义按钮原型 class:
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>
有没有人有关于如何将按钮逻辑与视图分开的提示、建议、链接等?这可能吗?
无需为此创建 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
来再次定位按钮。您应该重新使用您已经找到的按钮。