JEP 253:为模块化准备 JavaFX UI 控件和 CSS API
概述
为 JavaFX UI 控件和 CSS 功能定义公共 API,这些功能目前只能通过内部 API 获得,因此由于模块化的原因将变得不可访问。
目标
许多使用 JavaFX 的 UI 控件和 CSS 功能的开发者长期以来都忽略了避免使用内部 com.sun.*
API 的警告。在许多情况下,为了实现预期的效果,开发者别无选择,只能使用这些内部 API。随着 Java 9 即将发布,尤其是 Project Jigsaw 中模块之间引入了强边界,开发者会发现他们的代码将无法再编译或运行,因为 com.sun.*
包将不再可访问。本 JEP 的目标是为目前由内部 API 提供的功能定义公共 API。
非目标
鉴于模块化的意义,即即将无法访问 com.sun.*
包,因此无法以保留任何程度的向后兼容性的方式来实现这一点。因此,本 JEP 的目标并非保留向后兼容性。这并不意味着我们可以随意破坏任何内容;我们的意图仅是引入因强制执行模块边界而直接破坏的新 API(并演进现有的私有 API)。所有其他未受模块化影响的现有 API 将保持不变。
成功指标
成功可以通过两种方式来衡量:
-
依赖于 JavaFX 内部 API 的项目,特别是 Scene Builder、ControlsFX 和 JFXtras,在更新到新 API 后仍然可以正常工作,且功能没有任何损失。这三个项目均为开源项目,它们将提供一个极佳的测试平台,以确保所有必要的 API 都已提供。
-
围绕新 API 的讨论结果,例如控制行为所需的输入映射工作,带来了开发者长期以来所要求的功能改进。最终,如果一切按计划进行,第三方控件应该能够在不依赖任何内部 API 的情况下构建。
动机
如果不开展这项工作,许多项目将需要大幅减少所提供的功能,而对某些项目来说,这可能是致命的。例如,如果没有 Scene Builder 当前拥有的内部 API 访问权限,它可能难以维持可行性——毫无疑问,它在提供 CSS 样式功能和控件属性操作方面的能力将受到严重削弱,而这两项功能正是 Scene Builder 的核心功能。同样的论点也适用于大多数其他具有一定程度自定义控件或 CSS 实现的基于 JavaFX 的项目。
描述
该 JEP 被分解为两个半相关的子项目,每个子项目在实现最终目标的过程中都非常重要。这些项目并没有特定的执行顺序。
项目一:将 UI 控件皮肤制作成公共 API
目前,所有的皮肤都位于 com.sun.javafx.scene.control.skin
中。这意味着,第三方如果扩展了某个皮肤(例如,TextFieldSkin
)以添加额外功能、重写现有方法或修改控件皮肤的外观和行为,那么在 JDK 9 中他们的应用程序将无法正常运行。当然,这是依赖非公共 API 的用户的责任,但我们长期以来一直在讨论将此 API 公开,以便更好地支持第三方对 UI 控件的修改。
目的是将许多 JavaFX 控件皮肤移动到适当的公共包中,很可能是 javafx.scene.control.skin
。并不打算同时移动相关的行为类。
这项工作的绝大部分是对每个现有的皮肤类进行审查,确保以下内容:
-
皮肤之间存在 API 一致性,
-
存在高质量的 Javadoc 和单元测试,并且
-
每个类中的 API 通过将公共方法更改为私有方法保持在最低限度。
这项研究在单独的沙盒仓库中已经有了相当大的进展,尽管耗时,但只有几类值得关注的问题需要进一步分析,例如工具类、重复类和纯粹的实现类。除此之外,还有一些类可能有资格被引入到 javafx.scene.control
包中,或者至少需要进一步考虑,因为它们并非真正的皮肤。这些类包括 FXVK
(虚拟键盘)、ColorPalette
、CustomColorDialog
、DatePickerContent
和 InputField
。最后,有一些类,理想情况下方法应该设为私有的,但由于其他仅用于实现的类依赖于这些 API,因此无法这样做。针对所有这些问题,都会进行解决方案的研究,目前并不存在重大问题。
在 9 中将皮肤设为公共 API 的意图是为了确保它们的持续可用性。该 API 将有意保持在最低限度,并尽可能地简化,目的是在后续版本中根据开发人员的要求提供更有用的 API。众所周知,API(大部分)是永久性的,因此允许公共皮肤 API 在几个更新版本中逐步成熟似乎是最佳做法。
截至六月中旬,该项目已到达几乎所有的代码都已迁移并清理完毕的阶段。计划是在七月中旬到八月初之间的 JDK 9 构建版本中将其公开发布。以下是所有已迁移到 javafx.scene.control.skin
作为公共 API 的类的列表:
AccordionSkin
ButtonBarSkin
ButtonSkin
CellSkinBase
CheckBoxSkin
ChoiceBoxSkin
ColorPickerSkin
ComboBoxBaseSkin
ComboBoxListViewSkin
ComboBoxPopupControl
ContextMenuSkin
DateCellSkin
DatePickerSkin
HyperlinkSkin
LabelSkin
LabeledSkinBase
ListCellSkin
ListViewSkin
MenuBarSkin
MenuButtonSkin
MenuButtonSkinBase
NestedTableColumnHeader
PaginationSkin
ProgressBarSkin
ProgressIndicatorSkin
RadioButtonSkin
ScrollBarSkin
ScrollPaneSkin
SeparatorSkin
SliderSkin
SpinnerSkin
SplitMenuButtonSkin
SplitPaneSkin
TabPaneSkin
TableCellSkin
TableCellSkinBase
TableColumnHeader
TableHeaderRow
TableRowSkin
TableRowSkinBase
TableViewSkin
TableViewSkinBase
TextAreaSkin
TextFieldSkin
TextInputControlSkin
TitledPaneSkin
ToggleButtonSkin
ToolBarSkin
TooltipSkin
TreeCellSkin
TreeTableCellSkin
TreeTableRowSkin
TreeTableViewSkin
TreeViewSkin
VirtualContainerBase
VirtualFlow
以上列出的均为 JavaFX 中与用户界面组件皮肤(Skin)相关的类名,这些类用于定义和控制 JavaFX 控件的外观和行为。由于它们是专有名词,因此保留了原始英文名称。
这些类在六月中旬时,几乎剥离了所有未从 SkinBase
继承的 API。未来,计划根据早期访问版本的反馈逐步添加实用的 API。某些类,例如文本输入控件和虚拟化控件,已经具备额外的 API 以支持其功能。
项目二:审查并公开相关的 CSS API
与 Project One 一样,此项目旨在将目前位于 com.sun.*
包中的 API 公开发布。同样,这需要进行代码审查以尽量精简 API,同时还需要增加单元测试,并大幅完善文档。
这项工作的驱动因素将是继续允许 Scene Builder 在 JDK 9 中编译,并进行适当的修改。
截至六月中旬,该项目已进展到几乎所有代码都已迁移并清理完毕的阶段。计划是在七月中旬至八月初的 JDK 9 构建版本中将其公开发布。以下是所有已迁移到 javafx.css
的类的列表,它们作为公共 API:
CascadingStyle.java:public class CascadingStyle implements Comparable<CascadingStyle> {
CascadingStyle.java: public Style getStyle() {
CascadingStyle.java: public CascadingStyle(final Style style, Set<PseudoClass> pseudoClasses,
CascadingStyle.java: public String getProperty() {
CascadingStyle.java: public Selector getSelector() {
CascadingStyle.java: public Rule getRule() {
CascadingStyle.java: public StyleOrigin getOrigin() {
CascadingStyle.java: public ParsedValueImpl getParsedValueImpl() {
CompoundSelector.java:final public class CompoundSelector extends Selector {
CompoundSelector.java: public List<SimpleSelector> getSelectors() {
CompoundSelector.java: public CompoundSelector(List<SimpleSelector> selectors, List<Combinator> relationships)
CompoundSelector.java: public Match createMatch() {
CssError.java:public class CssError {
CssError.java: public static void setCurrentScene(Scene scene) {
CssError.java: public final String getMessage() {
CssError.java: public CssError(String message) {
CssError.java: public final static class PropertySetError extends CssError {
CssError.java: public PropertySetError(CssMetaData styleableProperty,
Declaration.java:final public class Declaration {
Declaration.java: public ParsedValue getParsedValue() {
Declaration.java: public String getProperty() {
Declaration.java: public Rule getRule() {
Rule.java:final public class Rule {
Rule.java: public final ObservableList<Declaration> getDeclarations() {
Rule.java: public final ObservableList<Selector> getSelectors() {
Rule.java: public Stylesheet getStylesheet() {
Rule.java: public StyleOrigin getOrigin() {
Selector.java:abstract public class Selector {
Selector.java: public Rule getRule() {
Selector.java: public void setOrdinal(int ordinal) {
Selector.java: public int getOrdinal() {
Selector.java: public abstract Match createMatch();
Selector.java: public abstract boolean applies(Styleable styleable);
Selector.java: public abstract boolean applies(Styleable styleable, Set<PseudoClass>[] triggerStates, int bit);
Selector.java: public abstract boolean stateMatches(Styleable styleable, Set<PseudoClass> state);
Selector.java: public static Selector createSelector(final String cssSelector) {
Selector.java: protected void writeBinary(DataOutputStream os, StringStore stringStore)
SimpleSelector.java:final public class SimpleSelector extends Selector {
SimpleSelector.java: public String getName() {
SimpleSelector.java: public List<String> getStyleClasses() {
SimpleSelector.java: public Set<StyleClass> getStyleClassSet() {
SimpleSelector.java: public String getId() {
SimpleSelector.java: public NodeOrientation getNodeOrientation() {
Size.java:final public class Size {
Size.java: public Size(double value, SizeUnits units) {
Size.java: public double getValue() {
Size.java: public SizeUnits getUnits() {
Size.java: public boolean isAbsolute() {
Size.java: public double pixels(double multiplier, Font font) {
Size.java: public double pixels(Font font) {
Size.java: public double pixels() {
Style.java:final public class Style {
Style.java: public Selector getSelector() {
Style.java: public Declaration getDeclaration() {
Style.java: public Style(Selector selector, Declaration declaration) {
Stylesheet.java:public class Stylesheet {
Stylesheet.java: public String getUrl() {
Stylesheet.java: public StyleOrigin getOrigin() {
Stylesheet.java: public void setOrigin(StyleOrigin origin) {
Stylesheet.java: public List<Rule> getRules() {
Stylesheet.java: public List<FontFace> getFontFaces() {
Stylesheet.java: public static Stylesheet loadBinary(URL url) throws IOException {
Stylesheet.java: public static void convertToBinary(File source, File destination) throws IOException {
CssParser.java:final public class CssParser {
CssParser.java: public CssParser() {
CssParser.java: public Stylesheet parse(final String stylesheetText) {
CssParser.java: public Stylesheet parse(final URL url) throws IOException {
CssParser.java: public Stylesheet parseInlineStyle(final Styleable node) {
CssParser.java: public ParsedValueImpl parseExpr(String property, String expr) {
CssParser.java: public static ObservableList<CssError> errorsProperty() {
概述
这两个项目的最终结果是创建了:
-
一个新的用于 UI 控件外观的包
javafx.scene.control.skin
,在经过审查、文档编写和测试后; -
必要的 CSS 相关类的迁移、审查、文档编写和测试。
测试
测试将仅限于那些没有特殊平台或硬件要求的附加单元测试。
风险与假设
主要风险是工作范围超出了预期。虽然已经做了一些研究来更好地理解需求,但不可否认的是,制作好的 API 将需要投入相当多的时间。