文章目录
前言
在IDEA中使用Maven创建并运行JavaFX项目
前置条件(必须)
- 电脑内有版本大于等于11(建议使用11或17长期支持版本)的jdk
- 环境变量JAVA_HOME设置的的jdk版本也要大于等于11
通过Maven原型创建项目:
如果在第四步中找不到对应原型需要手动添加:
接下来输入自己的项目信息:
编辑
archetypeArtifactId
,如果使用FXML就使用
javafx-archetype-fxml
,否则就使用
javafx-archetype-simple
。
之后再添加一个属性指定JavaFX的版本:
点击完成即可创建,创建后的项目结构如下:
pom文件如下:
<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.chinesecooly</groupId><artifactId>learn-javafx</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><dependency><groupId>org.openjfx</groupId><artifactId>javafx-controls</artifactId><version>17.0.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version><configuration><release>11</release></configuration></plugin><plugin><groupId>org.openjfx</groupId><artifactId>javafx-maven-plugin</artifactId><version>0.0.6</version><executions><execution><!-- Default configuration for running --><!-- Usage: mvn clean javafx:run --><id>default-cli</id><configuration><mainClass>com.chinesecooly.App</mainClass></configuration></execution></executions></plugin></plugins></build></project>
接下来使用compiler插件编译项目:
最后使用javafx插件运行项目:
运行结果如下:
javaFX设计思想
MVC模型
舞台和场景
一个JavaFX应用程序由一个或多个舞台(Stage)组成,一个舞台对应一个窗口。一个JavaFX应用程序有一个主要的舞台,它是由JavaFX运行时创建的。要想在舞台上显示任何东西,就必须为其附加场景(Scene),在运行时可以交换舞台的场景,但一个舞台同一时间只能展示一个场景。所有的视觉组件都要附加到场景上展示,这些视觉组件被称为节点,它们都继承自Node类,节点分为两类,第一类是分支节点,也叫做父节点,是可以包含其它节点的节点;另一类叫叶子节点,是不能包含其他节点的节点。所有附加到场景的节点总体被称为场景图(Scene Graph)。
生命周期
每个JavaFX应用都继承自Application类,该类的子类必须声明为公共的,并且必须有一个公共的无参数构造函数。当JavaFX应用启动时,会按顺序执行以下步骤:
- 启动JavaFX运行时环境。
- 构造Application类的实例。
- 执行
init()
方法。 - 执行
start(javafx.stage.Stage)
方法。 - 在调用
Platform.exit()
方法或最后一个窗口关闭并且implicitExit
属性为true
时执行exit()
方法。
线程模型
JavaFX创建一个应用程序线程,用于运行应用程序启动方法、处理输入事件和运行动画时间线。JavaFX舞台和场景的创建,以及对场景图的操作和修改,都必须在应用程序线程上完成。Java启动器加载并初始化应用程序线程上的指定的Application类。如果在Aplication类中没有main方法,或者main方法调用了Application.launch(),那么Application的一个实例就会在应用程序线程上构造。init方法是在启动器线程上调用的,而不是在应用程序线程上。这意味着应用程序不能在init方法中构造舞台或场景。在应用程序线程中,在事件调度、运行动画时间轴或任何其他代码期间发生的所有未处理异常都被转发到线程的异常捕获处理程序。
属性模型
每一个组件都有一些属性(成员变量),它们控制着组件的外观和行为,这些属性的类型不是简单的Java类型,而是属于JavaFX类型系统的包装类型,它们的继承结构如下:
在图中省略了一些一XxxExpress和XxxBase结尾的抽象中间类,它们主要的作用是提供一些方法的实现,方便后续的继承。并且图中还省略了大部分的关系线,仅保留了示例线(图中虚线箭头),因为其它类似的实现都是一样的。
ObservableValue和WritableValue框架
ObservableValue系列接口是一个包装类型框架,它包装了一个值并允许观察这个值的变化。
这个封装值可以用以下方法获取并且支持惰性求值。
TgetValue()
ObservableValue包装类型可以生成两种事件:更改事件和无效事件。更改事件指示值已经更改。无效事件指示当前值已经无效。可以通过以下方法为包装类型添加和移除事件监听器:
voidaddListener(InvalidationListener listener)voidaddListener(ChangeListener<?superT> listener)voidremoveListener(ChangeListener<?superT> listener)voidremoveListener(InvalidationListener listener)
WritableValue系列接口是一个包装类型框架,它包装了一个可以读取和设置的值。
TgetValue()voidsetValue(T value)
集合框架
集合框架没有对传统的Java集合实现类进行包装,而是直接继承了集合类的父接口(List,Map,Set),并且添加了三个默认方法:
defaultSortedList<E>sorted()defaultSortedList<E>sorted(Comparator<E> comparator)defaultFilteredList<E>filtered(Predicate<E> predicate)
所以集合框架不再是一个包装类型框架,因此在使用集合框架时JavaFX为其提供了具体的实现,而这些实现由FXCollections静态工具类提供,并且此类还提供了一些常用方法,可自行查阅。
属性实现:ReadOnlyProperty和Property
ReadOnlyProperty是一个只读包装类型,它实现了ObservableValue框架。
该接口定义了两个方法:
ObjectgetBean()StringgetName()
Property是一个可写包装类型,它实现了ReadOnlyProperty接口和ObservableValue框架。
该接口定义了可读属性的通用方法:
voidbind(ObservableValue<?extendsT> observable)voidbindBidirectional(Property<T> other)booleanisBound()voidunbind()voidunbindBidirectional(Property<T> other)
绑定属性实现:Binding
绑定属性由一个或多个依赖项组装而成。绑定属性观察其依赖项的变化,并根据依赖项的变化更新自己的值。绑定属性的值也是惰性计算的。
Binding是绑定属性的父类,它提供了一些绑定属性通用的方法:
voiddispose()//销毁绑定属性ObservableList<?>getDependencies()//返回绑定属性的依赖项voidinvalidate()//将绑定属性值标记为无效。这将强制在下一次请求时重新计算绑定属性的值。booleanisValid()//检查绑定属性是否是无效的
绑定属性可以通过两套API关联到它的依赖项:
- 低级API:使用繁琐,但功能强大、效率高。
- 高级API:使用简便,但功能缺失、效率低。
低级API的使用方式如下:
DoubleBinding bindingTest =newDoubleBinding(){{//依赖项super.bind(a, b, c, d);}//绑定属性的计算方法@OverrideprotecteddoublecomputeValue(){return a.getValue()* b.getValue()+ c.getValue()* d.getValue();}};
而抽象属性的高级API由Bindings工具类提供(以BooleanBinding为例):
staticBooleanBindingand(xxx)//绑定到与运算staticBooleanBindingbooleanValueAt(xxx)//绑定到集合的一个值staticBooleanBindingcreateBooleanBinding(xxx)//帮助函数,用于创建自定义布尔绑定。staticBooleanBindingequal(xxx)//绑定一个判等运算staticBooleanBindingnotEqual(double op1,ObservableNumberValue op2,double epsilon)//绑定一个不等运算staticBooleanBindinggreaterThan(xxx)//绑定一个大于运算staticBooleanBindinggreaterThanOrEqual(xxx)//绑定一个大于等于运算staticBooleanBindingisEmpty(xxx)//绑定一个判空运算static<K,V>BooleanBindingisNotEmpty(xxx)//绑定一个非空运算staticBooleanBindingisNull(ObservableObjectValue<?> op)//绑定一个判null运算staticBooleanBindingisNotNull(ObservableObjectValue<?> op)//绑定一个非null运算staticBooleanBindinglessThan(double op1,ObservableNumberValue op2)//绑定一个小与运算staticBooleanBindinglessThanOrEqual(double op1,ObservableNumberValue op2)//绑定一个小于等于运算staticBooleanBindingnot(ObservableBooleanValue op)//绑定一个非运算staticBooleanBindingselectBoolean(Object root,String... steps)//绑定到判断成员是否存在
监听
每当ObservableValue包装类型的包装值发生变化时,ChangeListener就会被通知。一个ChangeListener可以监听多个ObservableValue。
voidchanged(ObservableValue<?extendsT> observable,T oldValue,T newValue)//当ObservableValue的值发生变化时调用。
每当Observable的包装值变为无效时,InvalidationListener就会被通知。一个InvalidationListener可以监听多个Observable。
voidinvalidated(Observable observable)//
我们实现中的所有绑定都使用WeakInvalidationListener的实例,这意味着通常不需要处理绑定。但是,如果您计划在不支持WeakReferences的环境中使用应用程序,则必须销毁未使用的Bindings以避免内存泄漏。
顶层容器
Window
Window是诸如舞台、弹出窗口等顶层窗口的父类,它的属性如下:
Stage
finalvoidsetScene(Scene value)//设置场景对象finalvoidshow()//使Stage可见并立即退出该方法voidshowAndWait()//使Stage可见但阻塞至Satge关闭再退出该方法
一个Stage可以被另一个Stage所拥有。可以通过以下方法设置Stage的所有者:
finalvoidinitOwner(Window owner)//设置拥有者
可以通过以下方式设置Stage的窗口模式:
finalvoidinitModality(Modality modality)//设置模式
Stage的窗口模式决定表示Stage的窗口是否会阻塞同一JavaFX应用程序打开的其它窗口:
Modality.APPLICATION_MODAL//新创建的Stage窗口会阻塞在应用程序中打开的其它窗口Modality.NONE//新创建的Stage窗口不会阻塞在应用程序中打开的其它窗口Modality.WINDOW_MODAL//新创建的Stage将阻塞拥有新创建Stage的Stage窗口
可以通过以下方法设置Stage的样式:
finalvoidinitStyle(StageStyle style)//设置样式
所有的Stage装饰如下:
StageStyle.DECORATED//白色背景并带有操作系统装饰StageStyle.UNDECORATED//白色背景没有操作系统装饰StageStyle.TRANSPARENT//透明背景没有操作系统装饰StageStyle.UNIFIED//白色背景带有操作系统装饰,但工作区域和装饰区域没有分割线StageStyle.UTILITY//白色背景带有最简的操作系统装饰
弹出式窗口(PopupWindow)
弹出式窗口的父接口,Popup是它的一个实现,它的属性如下:
Popup
工具提示(Tooltip)
右键菜单(ContextMenu)
Screen
用于描述显示设备。
文件选择器(FileChooser)
FileChooser是一个文件选择器。
目录选择器(DirectoryChooser)
DirectoryChooser是一个目录选择器。
场景及场景图
Scene
Scene(场景)是场景图的容器,在实例化场景对象时必须设置它的
root
属性来指定场景图的根节点。场景在被附加到正在显示的窗口之前可以在任何线程上创建和修改。在附加之后只能JavaFX应用程序线程上修改。它的属性如下:
Node
场景图是一个由节点组成的树形结构,Node是场景图节点的基类,一个节点在场景图中只能出现一次,如果一个程序向父节点添加了一个子节点,并且该节点已经是不同父节点的子节点或场景的根节点,该节点将自动从其前父节点中移除,并且节点之间不能存在循环结构。和Scene类似,节点在被附加到正在显示窗口的场景之前可以在任何线程上创建和修改。在附加之后只能JavaFX应用程序线程上修改。以下是场景图节点的通用设计:
节点的属性如下:
布局节点(Parent)
布局节点内可以嵌套其它节点(控件节点和布局节点)。一旦其它节点被添加进来,布局节点将自动管理它门的布局,所以应用程序不应该直接定位或调整这些节点的大小。场景图同时支持可调整大小和不可调整大小的节点类。Node上的isResizable()方法返回给定的节点是否可以调整大小。一个可调整大小的节点类支持一个可接受的大小范围(minimum <= preferred <= maximum),允许它的父节点在布局期间根据父节点自己的布局策略和同级节点的布局需求在这个范围内调整它的大小。另一方面,不可调整大小的节点类没有一致的调整大小API,因此它们的父类在布局期间不会调整大小。应用程序必须通过在每个实例上设置适当的属性来确定不可调整大小的节点的大小。这些类返回它们的min、pref和max的当前布局边界,而resize()方法将成为一个无操作。可调整大小的类:Region, Control, WebView。不可调整大小的类:Group, Shape, Text。应用程序无法可靠地查询可调整大小的节点的边界,直到它被添加到场景中,因为节点的大小可能依赖于CSS。
Group
Region
Region是所有基于JavaFX node的UI控件和所有布局容器的基类。它是一个可调整大小的父节点。一个Region有一个Background和一个Border,尽管这两个可能都是空的。区域的背景由零个或多个BackgroundFills和零个或多个BackgroundImages组成。同样地,Region的边界由它的border定义,它由零个或多个BorderStrokes和零个或多个BorderImages组成。所有的背景填充都是首先绘制的,其次是背景图像,边框,最后是边框图像。默认情况下,Region继承其超类Parent的布局行为,这意味着它会将任何可调整大小的子节点调整到它们首选的大小,但不会重新定位它们。如果一个应用程序需要更具体的布局行为,那么它应该使用Region的子类之一。
水平布局(HBox)
垂直布局(VBox)
浮动布局(FlowPane)
网格布局(GridPane,TilePane)
控件节点(control)
控件节点在JavaFX应用程序中提供某种功能。例如,按钮、单选按钮、表格、树等。控件节点通常嵌套在一些JavaFX布局节点中。
文字显示(Text)
Text节点用于显示文本,JavaFX会根据以下规则对文本进行换行:
- 如果文本包含换行符
\n
- 如果文本宽度超过在Text节点上设置的换行宽度
按钮(ButtonBase)
ButtonBase是按钮类节点的基类,它提供了两个属性:
超链接(Hyperlink)
按钮(Button)
菜单按钮(MenuButton,SplitMenuButton)
开关(ToggleButton)
单选按钮(RadioButton)
复选框(CheckBox)
标签(Label)
下拉单项选择框(ChoiceBox)
下拉组合框(ComboBoxBase)
下拉单选自定义框(ComboBox)
日期选择框(DatePicker)
颜色选择框(ColorPicker)
文本输入框(TextInputControl)
单行文本输入框(TextField)
密码输入框(PasswordField)
多行文本输入框(TextArea)
滑块(Slider)
进度条(ProgressBar)
工具栏(ToolBar)
下拉菜单(MenuBar)
分割线(Separator)
分页(Pagination)
容器控件
折叠框(Accordion,TitledPane)
分割框(SplitPane)
选项卡(TabPane,Tab)
滚动窗格(ScrollPane)
视图控件
图片视图(ImageView)
ImageView节点用于显示由Image类加载的图像。
列表视图(ListView)
表格视图(TableView )
树形视图(TreeView)
属性表格视图(TreeTableView)
网页视图(WebView)
图表(Chart)
饼图(PieChart)
柱状图(BarChart)
分层柱状图(StackedBarChart)
散点图(ScatterChart)
折线图(LineChart)
面积图(AreaChart)
分层面积图(StackedAreaChart)
图形控件(Shape)
2D
3D
媒体控件(Media)
Audio
Video
HTML编辑器(HTMLEditor)
绘图(Canvas)
图片(Image)
字体(Font)
颜色(Paint)
效果(Effect)
转换(Transform)
动画(Animation)
用户模拟(Robot)
事件模型
ObservableValue包装类型产生的事件,就是JavaFX事件处理框架的实现,该框架的结构体系如下:
Event是JavaFX事件框架的顶级接口,在该接口中定义了一些事件的通用属性和方法:
staticfinalEventType<Event> ANY//所有事件类型的通用超类型。voidconsume()//将此事件标记为已使用EventcopyFor(Object newSource,EventTarget newTarget)//使用指定的事件源和目标创建并返回此事件的副本。staticvoidfireEvent(EventTarget eventTarget,Event event)//触发指定事件EventType<?extendsEvent>getEventType()//获取事件类型EventTargetgetTarget()//获取事件目标booleanisConsumed()//该事件是否被使用
每一个事件都关联着一个事件来源、事件类型(EventType)和事件目标(EventTarget)。事件来源是注册事件处理器(EventHandler)并将事件发送给处理器的对象。
//EventHandler中的方法voidhandle(T event)/特定类型事件的处方法
事件类型为事件提供了分类,所有的事件类型形成一个以
Event.ANY
为根的层次结构。基于这个层次结构,事件处理器可以注册一个超级事件类型,并接收它的子类型事件。(注意,不能构造两个具有相同名称和父类的不同EventType对象。)
finalStringgetName()//返回事件类型的名称finalEventType<?superT>getSuperType()//返回事件类型的超类型
事件目标(EventTarget)定义了事件发布时所经过的路径(EventDispatchChain):
EventDispatchChainbuildEventDispatchChain(EventDispatchChain tail)为此目标构造事件分发链。
EventDispatchChain可以将事件从一个事件调度器(EventDispatcher)传递到链中的下一个事件调度器。并且可以添加其它事件调度器到此链中。
EventdispatchEvent(Event event)//通过EventDispatchChain分派指定的事件。EventDispatchChainappend(EventDispatcher eventDispatcher)//将指定的事件调度器追加到此链。EventDispatchChainprepend(EventDispatcher eventDispatcher)//将指定的事件调度器前置到此链。
EventDispatcher可以将事件通过指定的EventDispatchChain分派给关联的EventTarget。一个事件处理器可以出现在多个事件处理器链中。
EventdispatchEvent(Event event,EventDispatchChain tail)
事件处理框架定义了事件交付的两个阶段。第一个阶段称为捕获阶段,当事件从与EventTarget关联的EventDispatchChain的第一个元素传递到最后一个元素时发生。第二阶段被称为冒泡阶段,其发生顺序与第一阶段相反。
顶层容器事件(WindowEvent)
鼠标键盘输入事件(input)
FXML
原文链接:https://openjfx.cn/javadoc/18/javafx.fxml/javafx/fxml/doc-files/introduction_to_fxml.html
FXML是一种可编写的、基于XML的用于构造JavaFX场景图的标记语言。在FXML中,一个FXML标签代表以下类型之一:
- 某个类的实例
- 某个类实例的属性
- 某个静态属性
- 一个定义代码块
- 一个脚本代码块
一个FXML属性表示以下类型之一:
- 某个类实例的属性
- 某个静态属性
- 事件处理程序
FXML标签
类实例
导入类
要想在FXML使用Java类,必须使用以下语句进行导入:
<?import javafx.scene.control.Label?>
通过标签创建
在FXML中创建实例最简单的方法是通过FXML元素。任何遵循JavaBean构造函数和属性命名约定的类都可以通过此方式创建实例。
<?import javafx.scene.control.Label?><Labeltext="Hello world FXML"/>
fx:value
fx:value
属性可以用来实例化没有默认构造函数但提供静态
valueOf(String)
方法的类:
<Stringfx:value="Hello, World!"/>
fx:factory
fx:factory
属性可以使用静态工厂方法实例类:
<FXCollectionsfx:factory="observableArrayList"><Stringfx:value="A"/><Stringfx:value="B"/><Stringfx:value="C"/></FXCollections>
构造器(Builder)
构造器可以用于实例化不符合Bean约定的类,FXML中的构造器支持由两个接口提供,
javafx.util.Builder
接口定义了一个名为
build()
的方法,它负责构造实际的对象:
publicinterfaceBuilder<T>{publicTbuild();}
javafx.util.BuilderFactory
接口负责生成能够实例化给定类型的构造器:
publicinterfaceBuilderFactory{publicBuilder<?>getBuilder(Class<?> type);}
JavaFX中提供了一个默认的构建器工厂
JavaFXBuilderFactory
,这个工厂能够创建和配置大多数不可变的JavaFX类型:
<Colorred="1.0"green="0.0"blue="0.0"/>
构建器构造的对象直到到达元素的结束标记时才会实例化。这是因为在元素被完全处理之前,所有必需的参数可能都不可用。
fx:include
<fx:include>
标签可以从在另一个文件中定义的FXML标签实例化对象:
<fx:includesource="filename"/>
<fx:include>
标签还支持用于指定应用于本地化所包含内容的资源包名称的属性,以及用于编码源文件的字符集:
<fx:includesource="filename"resources="resource_file"charset="utf-8"/>
fx:constant
<fx:constant>
标签用于创建对类常量的引用:
<Button><minHeight><Doublefx:constant="NEGATIVE_INFINITY"/></minHeight></Button>
fx:reference
<fx:reference>
标签可以创建对现有实例的引用:
<ImageView><image><fx:referencesource="myImage"/></image></ImageView>
实例属性
属性标签可以表示以下几种形式之一:
- 可读写(getter/setter)属性
- 只读列表(read-only list)属性
- 只读映射(read-only map)属性
可读写属性
如果属性元素表示的是可读写属性,那么属性元素的内容将作为可读写属性的setter的值传递:
<?import javafx.scene.control.Label?><Label><text>Hello, World!</text></Label>
只读列表属性
只读列表属性的getter返回java.util.List的一个实例,并且没有相应的setter方法。只读列表元素的内容在处理时自动添加到列表中:
<VBox><children><Label><text>line 1</text></Label><Label><text>line 2</text></Label></children></VBox>
只读映射属性
只读map属性的getter返回java.util.Map的一个实例,并且没有相应的setter方法。只读map元素的属性在处理结束标记时应用于映射。
<Button><propertiesfoo="123"bar="456"/></Button>
默认属性
JavaFX组件可以有一个默认属性。这意味着,如果FXML元素包含没有嵌套在属性元素中的子元素,则假定这些子元素属于默认属性。比如上文中的
<vBox>
完全可以改写成以下形式:
<VBox><Label><text>line 1</text></Label><Label><text>line 2</text></Label></VBox>
JavaFX组件的默认属性使用注释
@DefaultProperty(value="propertyName")
进行标记。
静态属性
静态属性是只有在特定上下文中才有意义的属性,因为它们不是应用到的类的固有属性,而是由另一个类(通常是控件的父容器)定义的。静态属性以定义它们的类的名称作为前缀来使用:
<GridPane><Label><text>my label</text><GridPane.rowIndex>0</GridPane.rowIndex><GridPane.columnIndex>0</GridPane.columnIndex></Label></TabPane>
定义代码块
<fx:define>
元素用于创建存在于场景图之外但可能需要在其它地方引用的对象。例如,在处理单选按钮时,通常需要定义一个ToggleGroup来管理按钮的选择状态。这个组不是场景图本身的一部分,所以不应该添加到按钮的父节点。定义块可以用来创建按钮组,而不影响文档的整体结构:
<VBox><fx:define><ToggleGroupfx:id="myToggleGroup"/></fx:define><children><RadioButtontext="A"toggleGroup="$myToggleGroup"/><RadioButtontext="B"toggleGroup="$myToggleGroup"/><RadioButtontext="C"toggleGroup="$myToggleGroup"/></children></VBox>
FXML属性
实例属性
FXML属性也可以用来配置实例的属性:
<?import javafx.scene.control.*?><Buttontext="Click Me!"/>
当属性值是复杂类型,不能使用简单的基于字符串的FXML属性表示时,或者值的字符长度太长,以至于将其指定为FXML属性会对可读性产生负面影响时,通常使用属性标签。FXML属性和属性标签一个区别在于FXML属性必须在各自标签到达结束标签时才会起作用,另一个区别在于FXML属性支持可以扩展其功能的操作符,支持的操作符如下:
- 位置解析操作符
- 资源解析操作符
- 变量解析操作符
- 绑定表达式
位置解析操作符
位置解析操作符(属性值前加前缀
@
)用于指定属性值应被视为相对于当前文件的URL(URL中的空白值必须经过编码),而不是一个简单的字符串:
<ImageView><image><Imageurl="@my_image.png"/></image></ImageView>
变量解析操作符
FXML文档定义了一个变量名称空间,在这个名称空间中,变量可以被唯一的标识。如果将
fx:id
属性值赋给标签,那么将在文档的名称空间中创建一个变量,变量解析操作符(在属性值前面加前缀
$
)允许调用者在调用相应的setter方法之前,将标签的属性值替换为指定变量的实例:
<fx:define><ToggleGroupfx:id="myToggleGroup"/></fx:define><RadioButtontext="A"toggleGroup="$myToggleGroup"/><RadioButtontext="B"toggleGroup="$myToggleGroup"/><RadioButtontext="C"toggleGroup="$myToggleGroup"/>
绑定表达式
绑定表达式(将属性值使用
${}
包裹)可以将属性的值绑定到其它属性上,这样当被绑定的属性值变化时,绑定的属性值也会变化:
<TextFieldfx:id="textField"/><Labeltext="${textField.text}"/>
绑定表达式支持的数据类型如下:
- 字符串
- 数字
- 布尔
绑定表达式支持的运算符如下:
- +、-、*、/
- &&、||、!
、>=、<、<=
- ==、!=
静态属性
FXML属性表示的静态属性和FXML标签标识的类似:
<GridPane><children><Labeltext="My Label"GridPane.rowIndex="0"GridPane.columnIndex="0"/></children></TabPane>
区别在于FXML属性表示的静态属性支持功能性操作符(绑定表达式除外)。
事件处理器属性
事件处理器属性是将事件处理器附加到FXML元素的一种方便方法,FXML支持三种类型的事件处理程序:
- 脚本事件处理器
- 控制器方法事件处理器
- 表达式事件处理器
脚本事件处理器
脚本事件处理程序是在事件触发时执行脚本代码的事件处理程序:
<?language javascript?><VBox><children><Buttontext="Click Me!"onAction="java.lang.System.out.println('You clicked me!');"/></children></VBox>
注意在代码片段开始时使用语言处理指令。这个指令告诉FXML加载程序应该使用脚本语言来执行事件处理程序。若要关闭脚本代码的自动编译,请放置处理指令
<?compile false?>
在包含脚本的标签的前面。若要再次启动脚本代码编译,请使用
<?compile true?>
。
控制器方法事件处理器
控制器方法事件处理器是由FXML文档的控制器定义的方法:
<VBoxfx:controller="com.foo.MyController"xmlns:fx="http://javafx.com/fxml"><children><Buttontext="Click Me!"onAction="#handleButtonAction"/></children></VBox>
通常情况下,事件处理器应该接受一个扩展自
javafx.event.Event
类型的单一参数,并返回
void
,当然,参数也可以省略。
packagecom.foo;publicclassMyController{publicvoidhandleButtonAction(ActionEvent event){System.out.println("You clicked me!");}}
表达式事件处理器
指向
javafx.event.EventHandler
类型的任何表达式都可以用作表达式处理程序:
<VBoxfx:controller="com.foo.MyController"xmlns:fx="http://javafx.com/fxml"><children><Buttontext="Click Me!"onAction="$controller.onActionHandler"/></children></VBox>
publicclassMyController{@FXMLpublicEventHandler<ActionEvent> onActionHandler =newEventHandler<>(){...}}
注意,其他类型的表达式,如绑定表达式,在此上下文中不受支持。
集合和属性的特殊处理程序
不能使用setOnEvent()方法监听集合的变化,因此,ObservableList,、ObservableMap和ObservableSet 使用一个一个特殊的onChange属性,该属性指向带有 ListChangeListener.Change,、MapChangeListener.Change、和 SetChangeListener.Change参数的处理方法。
<VBoxfx:controller="com.foo.MyController"xmlns:fx="http://javafx.com/fxml"><childrenonChange="#handleChildrenChange"/></VBox>
处理器方法如下:
packagecom.foo;importjavafx.collections.ListChangeListener.Change;publicclassMyController{publicvoidhandleChildrenChange(ListChangeListener.Change c){System.out.println("Children changed!");}}
也不能使用setOnEvent()方法监听属性的变化,要注册到一个属性,必须使用一个特殊的
on<propertyName>Change
属性:
<VBoxfx:controller="com.foo.MyController"xmlns:fx="http://javafx.com/fxml"onParentChange="#handleParentChange"/>
处理器方法如下:
publicclassMyController{publicvoidhandleParentChange(ObservableValue value,Parent oldValue,Parent newValue){System.out.println("Parent changed!");}}
脚本
<fx:script>
标记允许将脚本代码导入或嵌入FXML文件中。可以使用任何JVM脚本语言编写脚本。如果脚本引擎实现了
javax.script.Compilable
接口,那么脚本在第一次加载时将被默认编译。如果编译失败,FXMLLoader将退回到解释模式。
- 使用脚本的第一种方式是将脚本嵌入到关联的标签中:
<?language javascript?><?import javafx.scene.control.*?><?import javafx.scene.layout.*?><VBoxxmlns:fx="http://javafx.com/fxml"><fx:script>
function handleButtonAction(event) {
java.lang.System.out.println('You clicked me!');
}
</fx:script><children><Buttontext="Click Me!"onAction="handleButtonAction(event);"/></children></VBox>
- 使用脚本更常用的方式是引入外部的脚本文件:
<?language javascript?><?import javafx.scene.control.*?><?import javafx.scene.layout.*?><VBoxxmlns:fx="http://javafx.com/fxml"><fx:scriptsource="example.js"charset="cp1252"/><children><Buttontext="Click Me!"onAction="handleButtonAction(event);"/></children></VBox>
//example.jsfunctionhandleButtonAction(event){
java.lang.System.out.println('You clicked me!');}
控制器
虽然在脚本中编写简单的事件处理程序很方便,但通常更可取的做法是在编译后的强类型语言中定义更复杂的应用程序逻辑。如前所述,
fx:controller
属性允许将控制器类与FXML文档关联起来。控制器是一个编译类,它实现了文档定义的对象层次结构的背后代码。这个属性包含控制器类全类名。该类的一个实例在加载FXML文件时创建,因此控制器类必须有一个无参数的构造函数。
<VBoxfx:controller="com.foo.MyController"xmlns:fx="http://javafx.com/fxml"><children><Buttontext="Click Me!"onAction="#handleButtonAction"/></children></VBox>
packagecom.foo;publicclassMyController{publicvoidhandleButtonAction(ActionEvent event){System.out.println("You clicked me!");}}
在许多情况下,以这种方式简单地声明事件处理程序就足够了。然而,当需要对控制器及其管理的元素的行为进行更多的控制时,控制器可以定义一个
initialize()
方法,当其关联文档的内容被完全加载后,该方法将在实现控制器上调用:
publicvoidinitialize();
可以将FXML文件中的JavaFX组件绑定到控制器类中的字段,前提是需要为JavaFX组件的FXML元素提供一个
fx:id
属性,该属性具有要绑定的控制器字段的名称作为值。
publicclassMyFxmlController{publicLabel label;}
<VBoxxmlns:fx="http://javafx.com/fxml"><Labelfx:id="label"text="Line 1"/></VBox>
@FXML
注意,在前面的示例中,控制器成员字段和事件处理程序方法被声明为公共的,因此可以由加载器设置或调用它们。但是,对于那些喜欢限制控制器字段或处理程序方法可见性的开发人员,可以使用
@FXML
注解。这个注解将一个受保护的或私有的类成员标记为FXML可访问的。
嵌套的控制器
通过
<fx:include>
标签加载的嵌套FXML文档的控制器实例被直接映射到包含控制器的成员字段。例如,给定以下代码:
<VBoxfx:controller="com.foo.MainController"><fx:define><fx:includefx:id="dialog"source="dialog.fxml"/></fx:define></VBox>
publicclassMainControllerextendsController{@FXMLprivateWindow dialog;@FXMLprivateDialogController dialogController;}
当调用控制器的initialize方法时,dialog字段将包含从dialog.fxml中加载的根元素,dialogController字段将包含包含dialog对象的控制器。
FXMLLoader
FXMLLoader类负责实际加载FXML源文件并返回结果对象图。
URL location =getClass().getResource("example.fxml");ResourceBundle resources =ResourceBundle.getBundle("com.foo.example");FXMLLoader fxmlLoader =newFXMLLoader(location, resources);Pane root =(Pane)fxmlLoader.load();MyController controller =(MyController)fxmlLoader.getController();
在加载时,FXMLLoader使用com.sun.javafx.fxml.BeanAdapter的一个实例来包装一个实例化的对象,并调用它的Setter方法。这个私有类实现了java.util.Map接口,并允许调用者以键值对的形式获取和设置Bean属性值。如果一个元素表示一个已经实现了Map的类型,它不会被包装,它的get和put方法会被直接调用。
CSS
原文链接:https://openjfx.cn/javadoc/18/javafx.graphics/javafx/scene/doc-files/cssref.html
JavaFX允许使用CSS样式修饰JavaFX组件,JavaFX使用与Web CSS相同的CSS语法,但CSS属性是特定于JavaFX的,因此与Web对应的名称略有不同。除了少数例外,JavaFXCSS属性名的前缀是
-fx-
。CSS样式应用于JavaFX场景图中的节点,其方式类似于CSS样式应用于HTML DOM中的元素。样式首先应用于父类,然后应用于其子类。CSS选择器用于将样式与场景图节点相匹配。Node和CSS选择器的关系如下:
- Node的getTypeSelector方法返回一个String,类似于CSS类型选择器。默认情况下,此方法返回类的简单名称。注意,内部类或匿名类的简单名称可能不能用作类型选择器。在这种情况下,应该重写此方法以返回有意义的值。
- 场景图中的每个节点都有一个styleClass属性。注意,一个节点可能有多个样式类。Node的styleClass类似于可以出现在HTML元素上的class="…"属性。
- 场景图中的每个节点都有一个id变量,一个字符串。这类似于可以出现在HTML元素中的id="…"属性。
JavaFX默认样式表
JavaFX应用程序有一个默认的CSS样式表,它应用于所有的JavaFX组件。如果不提供组件的样式,默认的CSS样式表将对JavaFX组件进行样式设置。
Scene样式表
可以为JavaFX场景对象设置CSS样式表,这个样式表被应用于所有添加到场景图中的JavaFX组件。Scene样式表中指定的样式优先于默认样式表中指定的样式。
scene.getStylesheets().add("style1/button-styles.css");
布局组件样式表
在所有布局组件上也可以设置样式表,这个样式表应用于包含在布局组件内的所有组件,布局组件样式表指定的CSS样式通常优先于Scene样式表中指定的CSS样式。
Button button1 =newButton("Button 1");Button button2 =newButton("Button 2");VBox vbox =newVBox(button1, button2);
vbox.getStylesheets().add("style1/button-styles.css");
组件自身的CSS样式属性
可以通过在组件上直接设置CSS样式属性来设置组件的CSS样式,
此方式设置的样式优先于布局组件样式表中指定的样式。
Button button =newButton("Button 2");
button.setStyle("-fx-background-color: #0000ff");
JavaFX CSS样式属性一览
版权归原作者 超级种码 所有, 如有侵权,请联系我们删除。