是java实现图形界面的一种方式,其他还有java的awt、swing,但是逐渐被淘汰。
javafx可以实现逻辑和样式的分离,可以使用xml和css来编写样式。
在学习之前请确保你已经熟练掌握面向对象、包装类、枚举、注解、匿名对象等内容的概念和使用。
自从java11以后,jdk已经不内置javafx库,已交给开源社区管理,所以我们需要自己导入。
可以到这个网址去下载 jar 包。注意下载的类型是sdk。
使用maven的同学可以使用以下语句导入
- <dependency>
- <groupId>org.openjfx</groupId>
- <artifactId>javafx-controls</artifactId>
- <version>19-ea+8</version>
- </dependency>
本教程使用的java版本是18,因此可能与java8的写法有所出入。
第一种写法把fx app实现方法和main方法写在一个类里面,在java 11之后这个方式就失效了。
- import javafx.application.Application;
- import javafx.stage.Stage;
-
- public class _1helloJfx extends Application {
- public static void main(String[] args) {
- launch(args); //调用start
- }
-
- @Override
- public void start(Stage primaryStage) throws Exception {
- primaryStage.setTitle("javafx"); //设置标题
- primaryStage.show(); //展示窗口
-
- }
-
- }
第二种方法把两个方法分开放置,在launch里面传入有start方法类的反射。
- import javafx.application.Application;
-
- public class App{
- public static void main(String[] args) {
- Application.launch(Window.class, args);
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- import javafx.application.Application;
- import javafx.stage.Stage;
-
- public class Window extends Application {
- @Override
- public void start(Stage arg0) throws Exception {
- // TODO Auto-generated method stub
- arg0.show();
- }
- }
一个javafx程序有三层,最外面的是Stage层,一个Stage就是一个独立的窗口。
在往里是Scene层,一个Scene就是一个窗口内部的一个状态。
在往里就是一个一个的node节点,节点可以是按钮、标签、文本等组件。
任何一个事物都有产生、发展、消亡的过程,人是这样,程序也不例外。
javafx的生命周期分为init、start和stop,分别是初始化,正在运行和结束。
- @Override
- public void init() throws Exception {
- System.out.println("开始");
- System.out.println("init():" + Thread.currentThread().getName());
- }
-
- @Override
- public void start(Stage primaryStage) throws Exception {
- System.out.println("运行");
- primaryStage.show();
- System.out.println("start():" + Thread.currentThread().getName());
- }
-
- @Override
- public void stop() throws Exception {
- System.out.println("结束");
- System.out.println("stop():" + Thread.currentThread().getName());
- }
运行后发现init是一个独立的线程,start和stop同属一个JavaFX Application Thread线程,关闭窗口后就会进入stop,我们可以在这个方法里放一些停止连接之类的函数。
一个Stage类代表一个窗口,我们可以不使用方法提供的Stage,再写一个.
另外,Stage的setHeight()和setWidth()方法用来设置窗口的宽高。
- @Override
- public void start(Stage primaryStage) throws Exception {
- Stage stage = new Stage();
-
- stage.setHeight(500);
- stage.setWidth(500);
-
- stage.show();
-
- }
Stage类的其他方法如下表所示
| 方法 | 作用 |
|---|---|
| setMaxHeight(int); | 窗口最大高度 |
| setMaxWidth(int); | 窗口最大宽度 |
| setResizable(boolean); | 是否允许改变大小 |
| setMinHeight(int); | 窗口最小高度 |
| setMinWidth(int); | 窗口最小宽度 |
| setMaximized(boolean); | 是否最大化 |
| setIconified(boolean); | 是否最小化 |
| setFullScreen(boolean); | 是否全屏 |
| setAlwaysOnTop(boolean); | 窗口是否保持置顶 |
| setY(int) | 窗口出生距离屏幕上面的高度 |
| setX(int) | 窗口出生举例屏幕左边的宽度 |
| primaryStage.setOpacity(); | 透明度 |
| setTitle(String) | 设置窗口标题 |
| initStyle(StageStyle); | 设置窗口样式,只要知道三个常用的函数,StageStyle.DECORATED是正常,StageStyle.UTILITY没有最大最小化,StageStyle.TRANSPARENT是透明就行; |
| setScene(Scene) | 给窗口设置场景 |
| close(); | 关闭窗口 |
可以用监听来做界面自适应之类的东西。
窗口大小监听
- Stage类的其他方法如下表所示
-
- 方法 作用
- setMaxHeight(int); 窗口最大高度
- setMaxWidth(int); 窗口最大宽度
- setResizable(boolean); 是否允许改变大小
- setMinHeight(int); 窗口最小高度
- setMinWidth(int); 窗口最小宽度
- setMaximized(boolean); 是否最大化
- setIconified(boolean); 是否最小化
- setFullScreen(boolean); 是否全屏
- setAlwaysOnTop(boolean); 窗口是否保持置顶
- setY(int) 窗口出生距离屏幕上面的高度
- setX(int) 窗口出生举例屏幕左边的宽度
- primaryStage.setOpacity(); 透明度
- setTitle(String) 设置窗口标题
- initStyle(StageStyle); 设置窗口样式,只要知道三个常用的函数,StageStyle.DECORATED是正常,StageStyle.UTILITY没有最大最小化,StageStyle.TRANSPARENT是透明就行;
- setScene(Scene) 给窗口设置场景
- close(); 关闭窗口
-
-
- Srage宽度高度监听和位置监听
- 可以用监听来做界面自适应之类的东西。
-
- 窗口大小监听
窗口位置监听
- @Override
- public void start(Stage primaryStage) throws Exception {
-
- primaryStage.setY(100);
- primaryStage.setX(100);
- primaryStage.setWidth(200);
- primaryStage.setHeight(200);
-
- primaryStage.xProperty().addListener(new ChangeListener<Number>() {
-
- @Override
- public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
- System.out.println("举例屏幕左边有 = "+newValue.doubleValue()+" px");
- }
-
- });
-
- primaryStage.yProperty().addListener(new ChangeListener<Number>() {
-
- @Override
- public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
- System.out.println("举例屏幕上面有 = "+newValue.doubleValue()+" px");
- }
- });
-
- primaryStage.show();
在对应的包下放置文件,然后在getIcons的add方法里面填写相对路径。
package com.javafx;
import javafx.application.Application;
import javafx.scene.image.Image;
import javafx.stage.Stage;
public class Window extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.getIcons().add(new Image("com/javafx/ico-01.png"));
stage.show();
}
}
可以用initOwner()方法来设置一个窗口的父窗口,子窗口不关父窗口不会响应
public void start(Stage primaryStage) throws Exception {
// 模态化窗口。
Stage s1 = new Stage();
s1.setTitle("s1");
Stage s2 = new Stage();
s2.setTitle("s2");
// s1有s2;
s2.initOwner(s1);
s1.show();
s2.show();
}
在之前的练习中你可能发现了,窗口放大或缩小后,有些地方变成黑色,就是因为还没有添加一个Scene场景。
Group group = new Group(); Scene scene = new Scene(group); //创建一个场景 primaryStage.setScene(scene);//给窗口设置场景
创建一个场景需要传入一个参数,这个参数可以是一个布局,也可以是一个控件。
group是控件的集合,本身不是布局。
布局或集合组件.getChildren().add(组件对象1); //添加一个组件 布局或集合组件.getChildren().addAll(组件对象1, 组件对象2); //添加多个组件
常用信息输出组件
| Label() | 标签 |
|---|---|
| Text() | 文本 |
| TextFlow() | 文本域,fx8版本后出现 |
| ImageView | 图片框 |
| ProgressBar | 进度条 |
常用控制组件
| Button() | 按钮 |
|---|---|
常用信息输入组件
| TextField() | 文本单行输入框 |
|---|---|
| TextField() | 文本多行输入域 |
| RadioButton() | 单选按钮 |
| checkBox() | 多选按钮 |
| PasswordField() | 密码框 |
| slider() | 滑条 |
package com.javafx;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class Window extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("b1");
Label lab=new Label("标签");
Text t1=new Text("文本。。。");
Text t2=new Text();
t2.setText("文本2。。。");
TextFlow tf=new TextFlow();
tf.getChildren().addAll(t1,t2);
ProgressBar pb=new ProgressBar();
pb.setProgress(0.25);
ImageView iv=new ImageView();
iv.setImage(new Image("com/javafx/ico-01.png", 100, 100, true, true, true));
RadioButton rb1=new RadioButton();
RadioButton rb2=new RadioButton();
rb2.setLayoutX(20);
CheckBox cb=new CheckBox();
cb.setLayoutX(40);
Group group=new Group();
group.getChildren().addAll(rb1,rb2,cb);
TextField tfIn=new TextField();
TextArea ta=new TextArea();
PasswordField psf=new PasswordField();
FlowPane pane=new FlowPane();
pane.getChildren().addAll(btn,lab,tf,pb,iv,group,tfIn,ta,psf);
Scene scene=new Scene(pane);
primaryStage.setScene(scene);
primaryStage.setY(100);
primaryStage.setX(100);
primaryStage.setWidth(1000);
primaryStage.setHeight(600);
primaryStage.show();
}
}
布局请自行了解。这些布局之间可以互相嵌套,就像html里面的div一样。
| S.No | 形状和描述 |
|---|---|
| 1 | HBoxHBox布局将应用程序中的所有节点排列在一个水平行中。 |
| 2 | VBoxVBox布局将我们应用程序中的所有节点排列在一个垂直列中。 |
| 3 | BorderPane边框窗格布局将应用程序中的节点排列在顶部,左侧,右侧,底部和中心位置。 |
| 4 | StackPane堆栈窗格布局将应用程序中的节点排列在另一个上面,就像在堆栈中一样。 |
| 5 | TextFlow文本流布局在单个流中排列多个文本节点。 |
| 6 | AnchorPane“锚点”窗格布局将应用程序中的节点锚定在距窗格特定距离处。 |
| 7 | TilePaneTile窗格布局以均匀大小的tile的形式添加应用程序的所有节点。 |
| 8 | GridPane网格窗格布局将应用程序中的节点排列为行和列的网格。 |
| 9 | FlowPane流窗格布局包装流中的所有节点。 |
在所有节点上,都可以添加监听,以下是所有节点都通用的监听事件。
方式1
package com.javafx;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Window extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("b1");
btn.setOnAction(new javafx.event.EventHandler() {
@Override
public void handle(ActionEvent event) {
Button bu = (Button) event.getSource();
System.out.println(bu.getText() + "单击");
}
});
FlowPane pane=new FlowPane();
pane.getChildren().add(btn);
Scene scene=new Scene(pane);
primaryStage.setScene(scene);
primaryStage.setWidth(500);
primaryStage.setHeight(500);
primaryStage.show();
}
}
这个方法也可以改成单击
package com.javafx;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Window extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("b1");
btn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() {
@Override
public void handle(MouseEvent arg0) {
System.out.println("鼠标按键 = " + arg0.getButton().name());
Button bu = (Button) arg0.getSource();
//连续点击只有两次且用的是鼠标左键
if(arg0.getClickCount() == 2 &&
arg0.getButton().name().equals(MouseButton.PRIMARY.name())){
System.out.println(bu.getText()+"双击");
}
}
});
FlowPane pane=new FlowPane();
pane.getChildren().add(btn);
Scene scene=new Scene(pane);
primaryStage.setScene(scene);
primaryStage.setWidth(200);
primaryStage.setHeight(200);
primaryStage.show();
}
}
如果想监测到多个键同时按到,需要用另外一个方法,这个后面会讲
package com.javafx;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Window extends Application{
public Text text;
@Override
public void start(Stage primaryStage) throws Exception {
text=new Text("你按下了 “ ” 键。");
text.setFont(Font.font(25));
FlowPane pane=new FlowPane();
pane.getChildren().add(text);
Scene scene=new Scene(pane);
scene.setOnKeyPressed(new EventHandler(){
@Override
public void handle(KeyEvent arg0) {
if (arg0.getCode().getName().equals(KeyCode.A.getName())) {
System.out.println("A被按下");
}
text.setText("你按下了“"+arg0.getCode().getName()+"”键。");
}
});
primaryStage.setScene(scene);
primaryStage.setWidth(300);
primaryStage.setHeight(100);
primaryStage.show();
}
}