首页 > 基础资料 博客日记

全面详细的JavaFX国语核心教程(持续更新)

2023-07-24 10:54:17基础资料围观522

本篇文章分享全面详细的JavaFX国语核心教程(持续更新),对你有帮助的话记得收藏一下,看Java资料网收获更多编程知识

文章目录

前言

在IDEA中创建并运行JavaFX项目

方式一:IDEA提供的创建方式(推荐)

创建完成之后直接运行即可:

方式二:手动创建

前置条件(必须)

  • 电脑内有版本大于等于11(建议使用11或17长期支持版本)的jdk
  • 环境变量JAVA_HOME设置的的jdk版本也要大于等于11

通过Maven原型创建项目:

如果在第四步中找不到对应原型需要手动添加:

接下来输入自己的项目信息:

编辑archetypeArtifactId,如果使用FXML就使用javafx-archetype-fxml,否则就使用javafx-archetype-simple

之后再添加一个属性指定JavaFX的版本:

点击完成即可创建,创建后的项目结构如下:

pom文件如下:

<project xmlns="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核心思想

生命周期和线程模型

每个JavaFX应用都继承自Application类(子类必须声明为public,并且必须有一个公共的无参数构造函数)。当JavaFX应用启动时,会按顺序执行以下步骤:

  • 启动JavaFX运行时环境。
  • 构造Application类的实例。
  • 执行init()方法。
  • 执行 start(javafx.stage.Stage) 方法。
  • 等待JavaFX应用程序完成,直到出现以下两种情况停止等待:
    • 调用Platform.exit()方法
    • 最后一个窗口关闭并且implicitExit属性为true
  • 执行stop()方法。

一个简单的例子如下:

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

main()方法中调用了launch()方法,实际上JavaFX应用启动时的六个步骤就是由此方法发起的,通过分析源码得出以下线程模型:

JavaFX应用程序线程 启动线程 调用launch()方法创建启动线程 启动JavaFX运行时环境 发起构造Application实例的任务 执行任务,构造Application实例 执行Application实例的init()方法 发起执行start()方法的任务 执行任务,创建主Stage实例并执行Application实例的start()方法 等待应用程序完成 当调用Platform.exit()方法或最后一个窗口关闭并且implicitExit属性为true时发起执行stop()方法的任务 执行任务,执行Application实例的stop()方法,应用程序结束 JavaFX应用程序线程 启动线程

舞台(Stage)、场景(Scene)和场景图(Scene Graph)

一个JavaFX应用由一个或多个Stage组成,一个Stage对应一个窗口。但一个JavaFX应用程序有一个主要的Stage(在执行 start(javafx.stage.Stage) 方法时由应用程序线程创建并传入)。要想在Stage上显示任何东西,就必须为其附加Scene,在运行时可以交换Stage的Scene,但一个Stage同一时间只能展示一个Scene。所有的视觉组件都要附加到Scene上展示,这些视觉组件被称为节点,所有附加到场景的节点总体被称为Scene Graph。

JavaFX类型系统

和基本类型的包装类型类似,JavaFX有一套针对于Java原始类型的包装类型,包装类型分为以下两种:

  • ObservableValue:它包装了一个可以被读取和监听的值。
  • WritableValue:它包装了一个可以读取和设置的值。

ObservableValue

它的继承结构如下:

«interface»
ObservableValue<T>
«interface»
ObservableBooleanValue
«interface»
ObservableNumberValue
«interface»
ObservableObjectValue<T>
«interface»
ObservableDoubleValue
«interface»
ObservableFloatValue
«interface»
ObservableIntegerValue
«interface»
ObservableLongValue
«interface»
ObservableStringValue

这些包装子类和原始类型的关系如下:

原始类型包装类
booleanObservableBooleanValue
doubleObservableDoubleValue
floatObservableFloatValue
intObservableIntegerValue
longObservableLongValue
double, float, int, longObservableNumberValue
ObjectObservableObjectValue
StringObservableStringValue

ObservableValue接口提供了getValue方法用于获取获取包装类型的值,其子类接口提供了get方法用于获取原始类型的值。ObservableValue可以生成以下两种事件:

  • 更改事件:更改事件指示值已经更改。
  • 无效事件:无效事件指示当前值已经无效。

可以通过以下方法为ObservableValue添加和移除事件监听器:

void addListener(InvalidationListener listener)
void addListener(ChangeListener<? super T> listener)
void removeListener(ChangeListener<? super T> listener)
void removeListener(InvalidationListener listener)

WritableValue

它的继承结构如下:

«interface»
WritableValue
«interface»
WritableBooleanValue
«interface»
WritableNumberValue
«interface»
WritableObjectValue
«interface»
WritableDoubleValue
«interface»
WritableFloatValue
«interface»
WritableIntegerValue
«interface»
WritableLongValue
«interface»
WritableStringValue

这些包装子类和原始类型的关系如下:

原始类型包装类
booleanWritableBooleanValue
doubleWritableDoubleValue
floatWritableFloatValue
intWritableIntegerValue
longWritableLongValue
double, float, int, longWritableNumberValue
ObjectWritableObjectValue
StringWritableStringValue

ObservableValue接口类似,WritableValue接口提供了getValuesetValue方法用于获取和设置包装类型的值,其子类接口提供了getset方法用于获取和设置原始类型的值。

属性

每一个组件都有一些属性(成员变量),它们控制着组件的外观和行为,这些属性的类型不是简单的Java类型,而是基于JavaFX类型系统构建的包装类型。因此JavaFX类型系统是属性的基石。由于属性的重要性和复杂性,我将本节内容分为以下两部分:

  • 属性框架:基于ObservableValueWritableValue系列接口实现的属性框架,在实现上,属性框架只包含接口和抽象类。
  • 属性实现:基于属性接口实现的实体类,可在开发中直接使用。

它们之间的关系如下:

谁也不能保证将来会不会再出来其它的类型框架和实现。此外,由于本节类和接口继承和实现的复杂性和重复性,本节所涉及的类图会详略得当,不会展示完整的继承关系并且会省略类似XxxBase的中间类。整体结构大致如下:

属性框架

ReadOnlyProperty和Property

ReadOnlyProperty是只读属性,它实现了ObservableValue接口,它的继承关系如下:

ObservableValue
ReadOnlyProperty
ReadOnlyBooleanProperty
ReadOnlyDoubleProperty
ReadOnlyFloatProperty
ReadOnlyIntegerProperty
ReadOnlyLongProperty
ReadOnlyListProperty
ReadOnlyMapProperty
ReadOnlySetProperty
ReadOnlyObjectProperty
ReadOnlyStringProperty
ObservableValue
ObservableBooleanValue
BooleanExpression
ReadOnlyBooleanProperty
ReadOnlyProperty

该接口提供了以下几个方法:

//返回属性所在的对象,没有返回null
Object getBean()
//返回属性的名称,没有返回空字符串
String getName()

此外,一些实现子类还提供了以下几个方法:

//创建一个ReadOnlyObjectProperty来保存这个ReadOnlyXxxProperty的值
ReadOnlyObjectProperty<Xxx> asObject()
//返回一个包装了ReadOnlyProperty的ReadOnlyXxxProperty
static ReadOnlyXxxProperty readOnlyXxxProperty(ReadOnlyProperty<Xxx> property)

Property是可读可写属性,它实现了ReadOnlyPropertyWritableValue接口,它的继承关系如下:

WritableValue
Property
ReadOnlyProperty
BooleanProperty
DoubleProperty
FloatProperty
IntegerProperty
LongProperty
ListProperty
SetProperty
MapProperty
ObjectProperty
StringProperty
WritableValue
WritableBooleanValue
BooleanProperty
ObservableValue
ReadOnlyProperty
Property
ReadOnlyBooleanProperty

除了拥有ReadOnlyProperty所有功能外,所有Property都可以单向绑定到相同类型的ObservableValue,这意味着Property将始终包含与绑定的ObservableValue相同的值。还可以在两个Property之间定义双向绑定,以便两个属性始终包含相同的值。如果其中一个Property发生变化,另一个Property也会随之更新。该接口提供的方法如下:

//绑定到相同类型的ObservableValue
void bind(ObservableValue<? extends T> observable)
//绑定到其他属性
void bindBidirectional(Property<T> other)
//检查该属性是否绑定了其它属性
boolean isBound()
//解除该属性的所有单向绑定
void unbind()
//解除该属性与参数属性的双向绑定
void unbindBidirectional(Property<T> other)

此外,一些实现子类还提供了以下几个方法:

//创建一个双向绑定到该属性的ObjectProperty
ObjectProperty<Xxx> asObject()
//创建一个与参数双向绑定的XxxProperty
static XxxProperty xxxProperty(Property<Xxx> property)

ReadOnlyJavaBeanProperty和JavaBeanProperty

ReadOnlyJavaBeanPropertyJavaBeanProperty是充当常规Java Bean属性和相应JavaFX属性之间的适配器。其中:

  • ReadOnlyJavaBeanProperty是只读JavaBean属性和只读JavaFX属性的适配器。
  • JavaBeanProperty是可读写JavaBean属性和可读写JavaFX属性的适配器。

它们的继承结构如下:

ReadOnlyProperty
ReadOnlyJavaBeanProperty
ObservableValue
WritableValue
Property
JavaBeanProperty

集合框架

WritableObjectValue
WritableListValue
WritableMapValue
WritableSetValue
ObservableList
ObservableListValue
ObservableMap
ObservableMapValue
ObservableSet
ObservableSetValue
ObservableObjectValue

集合框架没有对传统的Java集合实现类进行包装,而是直接继承了集合类的父接口(List,Map,Set),并且添加了三个默认方法:

default SortedList<E> sorted()
default SortedList<E> sorted(Comparator<E> comparator)
default FilteredList<E> filtered(Predicate<E> predicate)

所以集合框架不再是一个包装类型框架,因此在使用集合框架时JavaFX为其提供了具体的实现,而这些实现由FXCollections静态工具类提供,并且此类还提供了一些常用方法,可自行查阅。

绑定属性

绑定属性由一个或多个依赖项组装而成,绑定属性观察其依赖项的变化,并根据依赖项的变化更新自己的值。Binding是绑定属性的顶层接口,它的继承关系如下:

ObservableValue
Binding
BooleanExpression
BooleanBinding
DoubleBinding
FloatBinding
IntegerBinding
ListBinding
LongBinding
MapBinding
NumberBinding
ObjectBinding
SetBinding
StringBinding
ObservableBooleanValue
BooleanExpression
ObservableNumberValue
NumberExpression
ObservableDoubleValue
DoubleExpression
ObservableObjectValue
ObjectExpression
ObservableFloatValue
FloatExpression
ObservableIntegerValue
IntegerExpression
ObservableLongValue
LongExpression
ObservableStringValue
StringExpression
ObservableListValue
ListExpression
ObservableMapValue
MapExpression
ObservableSetValue
SetExpression

该接口提供的方法如下:

void dispose()//销毁绑定属性
ObservableList<?> getDependencies()//返回绑定属性的依赖项
void invalidate()//将绑定属性值标记为无效。这将强制在下一次请求时重新计算绑定属性的值。
boolean isValid()//检查绑定属性是否是无效的

绑定属性可以通过两套API关联到它的依赖项:

  • 低级API:使用繁琐,但功能强大、效率高。
  • 高级API:使用简便,但功能缺失、效率低。

低级API的使用方式如下:

DoubleBinding bindingTest = new DoubleBinding() {

    {
    	//依赖项
        super.bind(a, b, c, d);
    }
	
	//绑定属性的计算方法
    @Override
    protected double computeValue() {
        return a.getValue() * b.getValue() + c.getValue() * d.getValue();
    }
};

而抽象属性的高级API由Bindings工具类提供(以BooleanBinding为例):

static BooleanBinding and(xxx)//绑定到与运算
static BooleanBinding booleanValueAt(xxx)//绑定到集合的一个值
static BooleanBinding createBooleanBinding(xxx)//帮助函数,用于创建自定义布尔绑定。
static BooleanBinding equal(xxx)//绑定一个判等运算
static BooleanBinding notEqual(double op1, ObservableNumberValue op2, double epsilon)//绑定一个不等运算
static BooleanBinding greaterThan(xxx)//绑定一个大于运算
static BooleanBinding greaterThanOrEqual(xxx)//绑定一个大于等于运算
static BooleanBinding isEmpty(xxx)//绑定一个判空运算
static <K,V> BooleanBinding isNotEmpty(xxx)//绑定一个非空运算
static BooleanBinding isNull(ObservableObjectValue<?> op)//绑定一个判null运算
static BooleanBinding isNotNull(ObservableObjectValue<?> op)//绑定一个非null运算
static BooleanBinding lessThan(double op1, ObservableNumberValue op2)//绑定一个小与运算
static BooleanBinding lessThanOrEqual(double op1, ObservableNumberValue op2)//绑定一个小于等于运算
static BooleanBinding not(ObservableBooleanValue op)//绑定一个非运算
static BooleanBinding selectBoolean(Object root, String... steps)//绑定到判断成员是否存在

属性实现

简单实现

所谓简单实现就是形如SimpleXxxProperty的实现类,它们的继承结构如下:

BooleanProperty
SimpleBooleanProperty
DoubleProperty
SimpleDoubleProperty
FloatProperty
SimpleFloatProperty
IntegerProperty
SimpleIntegerProperty
LongProperty
SimpleLongProperty
ListProperty
SimpleListProperty
SetProperty
SimpleSetProperty
MapProperty
SimpleMapProperty
ObjectProperty
SimpleObjectProperty
StringProperty
SimpleStringProperty
WritableValue
WritableBooleanValue
BooleanProperty
ObservableValue
ReadOnlyProperty
Property
ReadOnlyBooleanProperty
SimpleBooleanProperty

这些实现一般有以下几个构造方法:

SimpleXxxProperty()
SimpleXxxProperty(Xxx initialValue)
SimpleXxxProperty(Object bean, String name)
SimpleXxxProperty(Object bean, String name, Xxx initialValue)

包装器

所谓包装器就是形如ReadOnlyXxxWrapper的实现类,它们的继承结构如下:

SimpleBooleanProperty
ReadOnlyBooleanWrapper
SimpleDoubleProperty
ReadOnlyDoubleWrapper
SimpleFloatProperty
ReadOnlyFloatWrapper
SimpleIntegerProperty
ReadOnlyIntegerWrapper
SimpleLongProperty
ReadOnlyLongWrapper
SimpleListProperty
ReadOnlyListWrapper
SimpleSetProperty
ReadOnlySetWrapper
SimpleMapProperty
ReadOnlyMapWrapper
SimpleObjectProperty
ReadOnlyObjectWrapper
SimpleStringProperty
ReadOnlyStringWrapper
WritableValue
WritableBooleanValue
BooleanProperty
ObservableValue
ReadOnlyProperty
Property
ReadOnlyBooleanProperty
SimpleBooleanProperty
ReadOnlyBooleanWrapper

当实例化一个包装器类时,实际上它创建了两个同步的属性:

  • 只读属性:可以传递给外部用户。
  • 可读写属性(自身):应该只在内部使用。该接口提供

包装器的只读属性可以通过它提供的方法获取:

ReadOnlyXxxProperty getReadOnlyProperty()

包装器类一般有以下几个构造方法:

ReadOnlyXxxWrapper()
ReadOnlyXxxWrapper(Xxx initialValue)
ReadOnlyXxxWrapper(Object bean, String name)
ReadOnlyXxxWrapper(Object bean, String name, Xxx initialValue)

适配器

ReadOnlyJavaBeanBooleanProperty实现类的继承结构如下:

ReadOnlyJavaBeanProperty
ReadOnlyJavaBeanBooleanProperty
ReadOnlyJavaBeanDoubleProperty
ReadOnlyJavaBeanFloatProperty
ReadOnlyJavaBeanIntegerProperty
ReadOnlyJavaBeanLongProperty
ReadOnlyJavaBeanObjectProperty
ReadOnlyJavaBeanStringProperty
ObservableValue
ReadOnlyProperty
ReadOnlyJavaBeanProperty
ReadOnlyJavaBeanBooleanProperty

JavaBeanProperty实现类的继承结构如下:

ReadOnlyJavaBeanProperty
JavaBeanProperty
JavaBeanBooleanProperty
JavaBeanDoubleProperty
avaBeanFloatProperty
JavaBeanIntegerProperty
JavaBeanLongProperty
JavaBeanObjectProperty
JavaBeanStringProperty
ObservableValue
ReadOnlyProperty
WritableValue
Property
JavaBeanProperty
ReadOnlyJavaBeanProperty
JavaBeanBooleanProperty

对于适配器适配的Java Bean类,必须实现适配属性声明为public的Getter和Setter。如果Java Bean属性支持PropertyChangeListeners,那么适配器将意识到Java Bean属性的变化。否则,必须通过调用适配器的fireValueChangedEvent方法来通知它有关更改。实现类提供的方法如下:

//向JavaFX属性发出信号,表示它将不再被使用,并且可以删除任何引用。
void dispose()
//向所有附加的InvalidationListeners和changelistener发送通知。如果此属性的值发生变化,则需要调用此方法。
void fireValueChangedEvent()

值得注意的是不能直接创建适配器,必须通过JavaBeanXxxPropertyBuilder形式的建造者创建:

ReadOnlyJavaBeanBooleanPropertyBuilder
ReadOnlyJavaBeanDoublePropertyBuilder
ReadOnlyJavaBeanFloatPropertyBuilder
ReadOnlyJavaBeanIntegerPropertyBuilder
ReadOnlyJavaBeanLongPropertyBuilder
ReadOnlyJavaBeanObjectPropertyBuilder
ReadOnlyJavaBeanStringPropertyBuilder
JavaBeanBooleanPropertyBuilder
JavaBeanDoublePropertyBuilder
JavaBeanFloatPropertyBuilder
JavaBeanIntegerPropertyBuilder
JavaBeanLongPropertyBuilder
JavaBeanObjectPropertyBuilder
JavaBeanStringPropertyBuilder

建造者提供的方法如下:

//创建XxxBuilder实例
static XxxBuilder create()
//设置属性的名称。
XxxBuilder name(String name)
//设置适配器应该连接到的Java Bean实例。
XxxBuilder bean(Object bean)
//设置应该在其中搜索getter和setter的Java Bean类。
XxxBuilder beanClass(Class<?> beanClass)
//使用当前设置生成一个新的XxxProperty。
XxxProperty build()
//设置setter方法。
XxxBuilder setter(Method setter)
XxxBuilder setter(String setter)
//设置getter方法
XxxBuilder getter(Method getter)
XxxBuilder getter(String getter)

建造者的使用步骤如下:

  • 调用create方法生成一个建造者实例
  • 必须使用name方法指定属性的名称。
  • 如果属性的Getter和Setter不符合命名规范,则必须指明属性的Getter和Setter方法。
  • 如果要为某一个实例的属性生成适配器,则使用bean方法指定实例。
  • 如果要为某一个类的多个实例生成适配器,则使用beanClass方法指定类。
  • 最后使用build方法生成适配器。

事件模型

ObservableValue包装类型产生的事件,就是JavaFX事件处理框架的实现,该框架的结构体系如下:

Event是JavaFX事件框架的顶级接口,在该接口中定义了一些事件的通用属性和方法:

static final EventType<Event> ANY//所有事件类型的通用超类型。
void consume()//将此事件标记为已使用
Event copyFor(Object newSource, EventTarget newTarget)//使用指定的事件源和目标创建并返回此事件的副本。
static void fireEvent(EventTarget eventTarget, Event event)//触发指定事件
EventType<? extends Event> getEventType()//获取事件类型
EventTarget getTarget()//获取事件目标
boolean isConsumed()//该事件是否被使用

每一个事件都关联着一个事件来源、事件类型(EventType)和事件目标(EventTarget)。事件来源是注册事件处理器(EventHandler)并将事件发送给处理器的对象。

//EventHandler中的方法
void handle(T event)/特定类型事件的处方法

事件类型为事件提供了分类,所有的事件类型形成一个以Event.ANY为根的层次结构。基于这个层次结构,事件处理器可以注册一个超级事件类型,并接收它的子类型事件。(注意,不能构造两个具有相同名称和父类的不同EventType对象。)

final String getName()//返回事件类型的名称
final EventType<? super T> getSuperType()//返回事件类型的超类型

事件目标(EventTarget)定义了事件发布时所经过的路径(EventDispatchChain):

EventDispatchChain buildEventDispatchChain(EventDispatchChain tail)为此目标构造事件分发链。

EventDispatchChain可以将事件从一个事件调度器(EventDispatcher)传递到链中的下一个事件调度器。并且可以添加其它事件调度器到此链中。

Event dispatchEvent(Event event)//通过EventDispatchChain分派指定的事件。
EventDispatchChain append(EventDispatcher eventDispatcher)//将指定的事件调度器追加到此链。
EventDispatchChain prepend(EventDispatcher eventDispatcher)//将指定的事件调度器前置到此链。

EventDispatcher可以将事件通过指定的EventDispatchChain分派给关联的EventTarget。一个事件处理器可以出现在多个事件处理器链中。

Event dispatchEvent(Event event, EventDispatchChain tail)

事件处理框架定义了事件交付的两个阶段。第一个阶段称为捕获阶段,当事件从与EventTarget关联的EventDispatchChain的第一个元素传递到最后一个元素时发生。第二阶段被称为冒泡阶段,其发生顺序与第一阶段相反。

顶层容器事件

鼠标键盘输入事件

监听器

失效监听器

变化监听器

每当ObservableValue包装类型的包装值发生变化时,ChangeListener就会被通知。一个ChangeListener可以监听多个ObservableValue。

void changed(ObservableValue<? extends T> observable, T oldValue, T newValue)//当ObservableValue的值发生变化时调用。

每当Observable的包装值变为无效时,InvalidationListener就会被通知。一个InvalidationListener可以监听多个Observable。

void invalidated(Observable observable)//

我们实现中的所有绑定都使用WeakInvalidationListener的实例,这意味着通常不需要处理绑定。但是,如果您计划在不支持WeakReferences的环境中使用应用程序,则必须销毁未使用的Bindings以避免内存泄漏。

整体架构:MVVM

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?>
<Label text="Hello world FXML"/>

fx:value

fx:value属性可以用来实例化没有默认构造函数但提供静态valueOf(String)方法的类:

<String fx:value="Hello, World!"/>

fx:factory

fx:factory属性可以使用静态工厂方法实例类:

<FXCollections fx:factory="observableArrayList">
    <String fx:value="A"/>
    <String fx:value="B"/>
    <String fx:value="C"/>
</FXCollections>

构造器(Builder)

构造器可以用于实例化不符合Bean约定的类,FXML中的构造器支持由两个接口提供,javafx.util.Builder接口定义了一个名为build()的方法,它负责构造实际的对象:

public interface Builder<T> {
    public T build();
}

javafx.util.BuilderFactory接口负责生成能够实例化给定类型的构造器:

public interface BuilderFactory {
    public Builder<?> getBuilder(Class<?> type);
}

JavaFX中提供了一个默认的构建器工厂JavaFXBuilderFactory,这个工厂能够创建和配置大多数不可变的JavaFX类型:

<Color red="1.0" green="0.0" blue="0.0"/>

构建器构造的对象直到到达元素的结束标记时才会实例化。这是因为在元素被完全处理之前,所有必需的参数可能都不可用。

<fx:include>

<fx:include>标签可以从在另一个文件中定义的FXML标签实例化对象:

<fx:include source="filename"/>

<fx:include>标签还支持用于指定应用于本地化所包含内容的资源包名称的属性,以及用于编码源文件的字符集:

<fx:include source="filename" resources="resource_file" charset="utf-8"/>

<fx:constant>

<fx:constant>标签用于创建对类常量的引用:

<Button>
    <minHeight>
    	<Double fx:constant="NEGATIVE_INFINITY"/>
    </minHeight>
</Button>

<fx:reference>

<fx:reference>标签可以创建对现有实例的引用:

<ImageView>
    <image>
        <fx:reference source="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>
    <properties foo="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>
        <ToggleGroup fx:id="myToggleGroup"/>
    </fx:define>
    <children>
        <RadioButton text="A" toggleGroup="$myToggleGroup"/>
        <RadioButton text="B" toggleGroup="$myToggleGroup"/>
        <RadioButton text="C" toggleGroup="$myToggleGroup"/>
    </children>
</VBox>

FXML属性

实例属性

FXML属性也可以用来配置实例的属性:

<?import javafx.scene.control.*?>
<Button text="Click Me!"/>

当属性值是复杂类型,不能使用简单的基于字符串的FXML属性表示时,或者值的字符长度太长,以至于将其指定为FXML属性会对可读性产生负面影响时,通常使用属性标签。FXML属性和属性标签一个区别在于FXML属性必须在各自标签到达结束标签时才会起作用,另一个区别在于FXML属性支持可以扩展其功能的操作符,支持的操作符如下:

  • 位置解析操作符
  • 资源解析操作符
  • 变量解析操作符
  • 绑定表达式

位置解析操作符

位置解析操作符(属性值前加前缀@)用于指定属性值应被视为相对于当前文件的URL(URL中的空白值必须经过编码),而不是一个简单的字符串:

<ImageView>
    <image>
        <Image url="@my_image.png"/>
    </image>
</ImageView>

变量解析操作符

FXML文档定义了一个变量名称空间,在这个名称空间中,变量可以被唯一的标识。如果将fx:id属性值赋给标签,那么将在文档的名称空间中创建一个变量,变量解析操作符(在属性值前面加前缀$)允许调用者在调用相应的setter方法之前,将标签的属性值替换为指定变量的实例:

<fx:define>
    <ToggleGroup fx:id="myToggleGroup"/>
</fx:define>
<RadioButton text="A" toggleGroup="$myToggleGroup"/>
<RadioButton text="B" toggleGroup="$myToggleGroup"/>
<RadioButton text="C" toggleGroup="$myToggleGroup"/>

绑定表达式

绑定表达式(将属性值使用${}包裹)可以将属性的值绑定到其它属性上,这样当被绑定的属性值变化时,绑定的属性值也会变化:

<TextField fx:id="textField"/>
<Label text="${textField.text}"/>

绑定表达式支持的数据类型如下:

  • 字符串
  • 数字
  • 布尔

绑定表达式支持的运算符如下:

  • +、-、*、/
  • &&、||、!
  • >、>=、<、<=
  • ==、!=

静态属性

FXML属性表示的静态属性和FXML标签标识的类似:

<GridPane>
    <children>
        <Label text="My Label" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
    </children>
</TabPane>

区别在于FXML属性表示的静态属性支持功能性操作符(绑定表达式除外)。

事件处理器属性

事件处理器属性是将事件处理器附加到FXML元素的一种方便方法,FXML支持三种类型的事件处理程序:

  • 脚本事件处理器
  • 控制器方法事件处理器
  • 表达式事件处理器

脚本事件处理器

脚本事件处理程序是在事件触发时执行脚本代码的事件处理程序:

<?language javascript?>

<VBox>
    <children>
        <Button text="Click Me!" onAction="java.lang.System.out.println('You clicked me!');"/>
    </children>
</VBox>

注意在代码片段开始时使用语言处理指令。这个指令告诉FXML加载程序应该使用脚本语言来执行事件处理程序。若要关闭脚本代码的自动编译,请放置处理指令<?compile false?>在包含脚本的标签的前面。若要再次启动脚本代码编译,请使用<?compile true?>

控制器方法事件处理器

控制器方法事件处理器是由FXML文档的控制器定义的方法:

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="#handleButtonAction"/>
    </children>
</VBox>

通常情况下,事件处理器应该接受一个扩展自javafx.event.Event类型的单一参数,并返回void,当然,参数也可以省略。

package com.foo;

public class MyController {
    public void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}

表达式事件处理器

指向javafx.event.EventHandler类型的任何表达式都可以用作表达式处理程序:

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="$controller.onActionHandler"/>
    </children>
</VBox>
public class MyController {

    @FXML
    public EventHandler<ActionEvent> onActionHandler = new EventHandler<>() { ... }

}

注意,其他类型的表达式,如绑定表达式,在此上下文中不受支持。

集合和属性的特殊处理程序

不能使用setOnEvent()方法监听集合的变化,因此,ObservableList,、ObservableMap和ObservableSet 使用一个一个特殊的onChange属性,该属性指向带有 ListChangeListener.Change,、MapChangeListener.Change、和 SetChangeListener.Change参数的处理方法。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children onChange="#handleChildrenChange"/>
</VBox>

处理器方法如下:

package com.foo;

import javafx.collections.ListChangeListener.Change;

public class MyController {
    public void handleChildrenChange(ListChangeListener.Change c) {
        System.out.println("Children changed!");
    }
}

也不能使用setOnEvent()方法监听属性的变化,要注册到一个属性,必须使用一个特殊的on<propertyName>Change属性:

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml" onParentChange="#handleParentChange"/>

处理器方法如下:

public class MyController {
    public void handleParentChange(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.*?>

<VBox xmlns:fx="http://javafx.com/fxml">
    <fx:script>
    function handleButtonAction(event) {
       java.lang.System.out.println('You clicked me!');
    }
    </fx:script>

    <children>
        <Button text="Click Me!" onAction="handleButtonAction(event);"/>
    </children>
</VBox>
  • 使用脚本更常用的方式是引入外部的脚本文件:
<?language javascript?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns:fx="http://javafx.com/fxml">
    <fx:script source="example.js" charset="cp1252"/>

    <children>
        <Button text="Click Me!" onAction="handleButtonAction(event);"/>
    </children>
</VBox>
//example.js
function handleButtonAction(event) {
   java.lang.System.out.println('You clicked me!');
}

控制器

虽然在脚本中编写简单的事件处理程序很方便,但通常更可取的做法是在编译后的强类型语言中定义更复杂的应用程序逻辑。如前所述,fx:controller属性允许将控制器类与FXML文档关联起来。控制器是一个编译类,它实现了文档定义的对象层次结构的背后代码。这个属性包含控制器类全类名。该类的一个实例在加载FXML文件时创建,因此控制器类必须有一个无参数的构造函数。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="#handleButtonAction"/>
    </children>
</VBox>
package com.foo;

public class MyController {
    public void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}

在许多情况下,以这种方式简单地声明事件处理程序就足够了。然而,当需要对控制器及其管理的元素的行为进行更多的控制时,控制器可以定义一个initialize()方法,当其关联文档的内容被完全加载后,该方法将在实现控制器上调用:

public void initialize();

可以将FXML文件中的JavaFX组件绑定到控制器类中的字段,前提是需要为JavaFX组件的FXML元素提供一个fx:id属性,该属性具有要绑定的控制器字段的名称作为值。

public class MyFxmlController {

    public Label label;

}
<VBox  xmlns:fx="http://javafx.com/fxml" >
    <Label fx:id="label" text="Line 1"/>
</VBox>

@FXML

注意,在前面的示例中,控制器成员字段和事件处理程序方法被声明为公共的,因此可以由加载器设置或调用它们。但是,对于那些喜欢限制控制器字段或处理程序方法可见性的开发人员,可以使用@FXML注解。这个注解将一个受保护的或私有的类成员标记为FXML可访问的。

嵌套的控制器

通过<fx:include>标签加载的嵌套FXML文档的控制器实例被直接映射到包含控制器的成员字段。例如,给定以下代码:

<VBox fx:controller="com.foo.MainController">
   <fx:define>
      <fx:include fx:id="dialog" source="dialog.fxml"/>
   </fx:define>
</VBox>
public class MainController extends Controller {
    @FXML private Window dialog;
    @FXML private DialogController 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 = new FXMLLoader(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 = new Button("Button 1");
Button button2 = new Button("Button 2");
VBox vbox = new VBox(button1, button2);
vbox.getStylesheets().add("style1/button-styles.css");

组件自身的CSS样式属性

可以通过在组件上直接设置CSS样式属性来设置组件的CSS样式,
此方式设置的样式优先于布局组件样式表中指定的样式。

Button button = new Button("Button 2");
button.setStyle("-fx-background-color: #0000ff");

一个超级简单且全面的demo:计算器

项目地址:FX计算器

顶层容器

Window

Window是诸如舞台、弹出窗口等顶层窗口的父类,它的属性如下:


Stage

final void setScene(Scene value)//设置场景对象
final void show()//使Stage可见并立即退出该方法
void showAndWait()//使Stage可见但阻塞至Satge关闭再退出该方法

一个Stage可以被另一个Stage所拥有。可以通过以下方法设置Stage的所有者:

final void initOwner(Window owner)//设置拥有者

可以通过以下方式设置Stage的窗口模式:

final void initModality(Modality modality)//设置模式

Stage的窗口模式决定表示Stage的窗口是否会阻塞同一JavaFX应用程序打开的其它窗口:

Modality.APPLICATION_MODAL//新创建的Stage窗口会阻塞在应用程序中打开的其它窗口
Modality.NONE//新创建的Stage窗口不会阻塞在应用程序中打开的其它窗口
Modality.WINDOW_MODAL//新创建的Stage将阻塞拥有新创建Stage的Stage窗口

可以通过以下方法设置Stage的样式:

final void initStyle(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)

JavaFX CSS样式属性一览


文章来源:https://blog.csdn.net/qq_45295475/article/details/125736509
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云