一般建议
我建议不要尝试直接从抽象概念开始工作,而是在现有代码中查看在 JavaFX 中实现的这些模式的示例,并了解它们是如何在那里编码的。当你找到一些你喜欢的模式时,你可以在你的代码中模拟它们。
在大型编程中,您在整个应用程序或场景中应用 MVC 模式,而在小型编程中,您在特定控件类型中应用模式是不同的。您的问题似乎与后一种情况更相关,因此我将在此答案中主要考虑这一点。
JavaFX 控件 - 皮肤、行为和公共 API
查看 JavaFX 的现有控件代码。对于 a Button
,您将拥有:
- javafx.scene.control.Button - 用于操作控件的公共 API。
- javafx.scene.control.skin.ButtonSkin - 控件的可视化视图。
- javafx.scene.control.behavior.ButtonBehavior - 事件绑定(键/鼠标事件等)。
提供ButtonBehavior
了一个抽象 api,以便一个按钮可以绑定到不同的平台和用户特定的事件映射,用于键、鼠标和触摸组合。
ButtonSkin
允许在不修改公共 API 或行为的情况下完全换入和换出控件的可视化实现。
Button
公共 API 提供了一个稳定的 API,按钮的用户可以对其进行编程,以独立于控件实现的任何视觉或行为变化。
删除抽象
虽然皮肤和行为 API 非常适合抽象出行为和外观的实现细节,但它们确实增加了控件实现者的复杂性和开销。您应该考虑您正在创建的控件是否真的需要这种复杂性并评估替代的、更简单的方法。
最简单的方法是扩展现有的窗格并将所有逻辑放在单个扩展类中(或者甚至只是提供构造控制节点的工厂方法)。您失去了抽象以及简单实现可互换性和关注点分离的好处,但是您有一个非常简单的实现。Oracle Mastering FXML 教程中的自定义控件示例就是这种直接扩展机制的一个示例。
我使用的一种稍微复杂的方法是使用一个封装公共 API 和控制状态信息的类以及一个单独的皮肤类来处理事件或 UI 呈现。我喜欢这种方法,因为它清楚地将 JavaFX 特定逻辑和代码与底层控件状态和公共 API 分开,而不会像标准 JavaFX 控件的行为/皮肤/公共 API 方法那样增加太多的概念开销。使用这种方法的一些示例代码在这个井字游戏中。
皮肤/行为和公共 API 之间的通信
利用包含控件公共 API 的类中的JavaFX 属性机制。这些属性可以定义控件的公共 API,也可以通过ReadOnlyWrappers等类型封装内部控件状态。
皮肤可以将绑定和侦听器应用于属性,以便在触发侦听器时,皮肤可以触发自身的自动更新以反映新的控件状态。控件的用户可以侦听或绑定到控件的内部状态,以便在该状态更改时采取行动。这种方法的一个示例是ToggleButton 的 selected 属性,其中皮肤会根据按钮是否被选中来改变切换按钮的外观。
当您创建皮肤时,使用对包含控件的公共 api 的反向引用来初始化皮肤和行为(例如SymbolSkin
,并SymbolBehaviour
具有对 的引用Symbol
)。SymbolSkin 或 SymbolBehavior 中的 mouseclickhandler 可以拦截鼠标单击,并将公共 api 上的选定属性切换到 Symbol,然后侦听符号上选定属性的更改的用户代码可以在该用户代码不紧密的情况下生效耦合到符号的皮肤或行为。
公共 APISymbol
只需要公开一个方法,该方法getSkin()
将皮肤作为一个简单的节点返回,可以在场景图中使用。Symbol
通过这样做,皮肤和行为的实现对使用公共 API的调用类隐藏。
扩展 Region 并使用 CSS
通过让控件皮肤 extend Region
,控件变得可以通过 css 设置样式。我强烈推荐这种方法,因为这意味着控件的许多视觉方面(例如颜色填充、背景、边框、形状等)都可以在JavaFX 丰富的 css 样式语言中进行操作。
执行此操作时,请尝试使用单独的 css 样式类来检测控件外观中的所有节点。这为控件的可视组件提供了挂钩,您可以使用这些挂钩来设置这些组件的样式。使用 CSS 设置图表样式的 Oracle 教程是这种对复杂组件进行样式设置的示例。
要在您的问题中定义不同的符号,您可以创建一个Symbol
控件。定义一个SymbolBehaviour
用于处理它的用户事件和一个SymbolSkin
扩展Region
。每个符号实例都可以分配有自己的样式类,公共符号 API 可以提供构造函数或工厂方法,在符号皮肤的区域上设置此样式类。使用-fx-shape
css 说明符根据 svg 路径字符串设置符号形状的样式。
css 样式类名称本身可以硬编码到您的SymbolSkin
实现中,并作为公共 API 的一部分进行记录(如标准控件的 CSS 参考指南中所做的那样)。
使用定义控件默认样式的默认 css 模板运送您的控件。
FXML 注意事项
当您有一个由许多需要协调的子控件组成的大型应用程序或场景时,您可以使用FXML之类的东西以声明性方式定义场景。这允许您将 UI 设计与您的命令式编程代码库分开,并确保您不会开始在视图中编写命令式代码。
对于具有 FXML 的真正 MVC 架构,您可能需要的不仅仅是 FXML 文件和用于场景实现的支持控制器,您还需要更高级别的控制机制来换入和换出场景,可能还需要更复杂的事件处理方法,例如作为事件总线,但这超出了这个问题的范围。
有关的
有一个略有不同但相关的问题,应该可以帮助您更好地理解问题域(JavaFX 2.0 - 样式/模板现有控件)。