

PreferencesFX自定义Setting的实现案例
source link: http://afoo.me/posts/2021-01-17-preferenesfx-custom-setting-control.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

2021-01-17
PreferencesFX其实对常见类型的Setting有默认支持,比如字符串,数字,选择列表等, 甚至于也支持File/Directory类型的Setting, 允许我们使用FileChooser来选择和设定对应的Setting状态与展示, 但是,这几天想添加一个字体的Setting配置项, 发现默认的搞不定,但还想继续沿用PreferencesFX的基础设施,所以研究了下如何自定义PreferencesFX的Setting。
PreferencesFX其实提供了两种自定义Setting的扩展机制:
Setting.of(Node) Setting.of(description, Field, Property)
首先,我们的Font类型的自定义Setting在展示的时候, 预期的展示是这样的: 一个TextField作为字体类型, 大小和风格的选择结果展示, 一个Button, 当点击的时候,则打开一个字体选择对话框(我们使用ControlsFX的DialogSeletorDialog), 用户选择了相应字体之后, 则将所选择的字体信息格式化之后设置给TextField并更新对应的Property, 至于Setting的显示名称,则直接使用传入的description即可。
在这个前提下,我们首先得先定义一个SimpleControl, 下面是我们的实现:
import com.dlsc.formsfx.model.structure.StringField import com.dlsc.preferencesfx.formsfx.view.controls.SimpleControl import com.keevol.keenotes.desk.utils.FontStringConverter import javafx.geometry.Insets import javafx.scene.control.{Button, TextField} import javafx.scene.layout.{HBox, Priority, StackPane} import javafx.scene.text.Font import org.controlsfx.dialog.FontSelectorDialog /** * a custom simple control for font with foot chooser * * @author fq@keevol.com */ class SimpleFontControl extends SimpleControl[StringField, StackPane] { var textField: TextField = _ var fontChooseButton: Button = _ val fontStringConverter = new FontStringConverter() override def initializeParts(): Unit = { super.initializeParts() node = new StackPane() textField = new TextField() textField.setEditable(false) fontChooseButton = new Button("Choose Font") fontChooseButton.setOnAction(e => { val dialog = new FontSelectorDialog(Font.getDefault) val p = dialog.showAndWait() if (p.isPresent) { val font = p.get() println("font.toString: " + font.toString) textField.setText(fontStringConverter.toString(font)) } }) val hbox = new HBox(10) hbox.setPadding(new Insets(3)) hbox.getChildren.addAll(textField, fontChooseButton) HBox.setHgrow(textField, Priority.ALWAYS) node.getChildren.add(hbox) } override def layoutParts(): Unit = { } override def setupBindings(): Unit = { super.setupBindings() // without this, PreferencesFX will throw exception. if (field.valueProperty.get == "null" || field.valueProperty.get == null) field.valueProperty.set("") field.valueProperty().bindBidirectional(textField.textProperty()) } }
这几个override的方法原则上 layoutParts()
是必需的, 但我们把这个方法的一些逻辑直接合并到了initializeParts()方法中(即组件的初始化和layout放一起了)。
因为Font和String类型差异,我们将Font到String的格式化逻辑以及从String创建Font的逻辑抽象封装到了FontStringConverter(一个StringConverter实现):
package com.keevol.keenotes.desk.utils import javafx.scene.text.{Font, FontWeight} import javafx.util.StringConverter import org.apache.commons.lang3.StringUtils class FontStringConverter extends StringConverter[Font] { override def toString(font: Font): String = s"${font.getFamily}, ${font.getSize}, ${font.getStyle}" override def fromString(fontString: String): Font = { val fontFamily = StringUtils.substringBefore(fontString, ",") val fontSize = StringUtils.substringBetween(fontString, ", ", ", ") val fontStyle = StringUtils.substringAfterLast(fontString, ", ") val f = if (StringUtils.contains(fontStyle.toLowerCase, "bold")) { Font.font(fontFamily, FontWeight.BOLD, fontSize.toDouble) } else { Font.font(fontFamily, fontSize.toDouble) } f } }
有了这些之后, 我们就可以添加Font类型的自定义Setting到PreferencesFX了:
val fontProperty = new SimpleStringProperty("Serif") ... Setting.of("Font", Field.ofStringType(fontProperty).render(new SimpleFontControl()), fontProperty)
现在, 我们的主程序就可以基于这个Setting做初始化了:
def tile(channel: String, content: String, dt: Date = new Date()) = { val card = new KeeNoteCard card.title.setText(channel + s"@${DateFormatUtils.format(dt, "yyyy-MM-dd HH:mm:ss")}") card.content.setText(content) Bindings.bindBidirectional(settings.fontProperty, card.content.fontProperty(), new FontStringConverter()) card }
card.content是一个Label,所以它的fontProperty()就是Font类型的ObjectProperty, 因为与Settings中的fontProperty(SimpleStringProperty类型)类型不同,所以我们使用了Bindings.bindBidirectional配合FontStringConverter完成了设定与应用组件之间的状态绑定。
当然,这种方法不一定是最好或者最符合PreferencesFX设计哲学的方式, 但却是最符合我编码胃口的方式,起码SimpleFontControl和FontStringConverter把通用逻辑都封装的差不多了。

Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK