官网:https://openjfx.io/
javafx中文资料:http://www.javafxchina.net/main/
javafx 8版本文档:https://docs.oracle.com/javase/8/javase-clienttechnologies.htm
javafx 8版本api文档:https://docs.oracle.com/javase/8/javafx/api/toc.htm
(1)去官网下载jdk,并解压到相应的位置。
下载链接:https://gluonhq.com/products/javafx/
(2)引入项目
Graphics Device initialization failed for : d3d, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pip
解决:看看javafx下载的版本对不对,看看是不是下载成Linux或者用的是x64还是x86
Exception in Application start method
java.lang.reflect.InvocationTargetException
在IDEA里面设置一下路径,具体设置方式如下:
下图该选项输入:
--module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.fxml
一个JavaFX应用可以包含多个Stage。
一个Stage只能包含一个Scene,但是Scence可以切换。
public class Test1 extends Application {
public static void main(String[] args) {
// launch方法会自动调用start方法
Application.launch(args);
}
@Override
public void init() throws Exception {
super.init();
// 首先执行,比如访问数据库什么的
System.out.println("init");
}
@Override
public void start(Stage stage) throws Exception {
System.out.println("start");
Label label = new Label("hello world!");
BorderPane pane = new BorderPane(label);
Scene scene = new Scene(pane, 300, 300);
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
System.out.println("start end");
}
@Override
public void stop() throws Exception {
super.stop();
// 只有正常关闭才会执行,如果点击编译器的终止,不执行该方法
// 最后释放资源的代码
System.out.println("stop");
}
}
说明:
一般都是设置Scene的大小,让Stage去适应Scene,而不用单独设置自己的宽高
四种选择:
①StageStyle.DECORATED - 默认
有装饰的纯白背景
②StageStyle.UNDECORATED
a.没有装饰的纯白背景
b.没有最上面图标、名字、最小化、最大化、关闭按钮
c.如果没有放Scene,什么都不显示
③StageStyle.TRANSPARENT
背景透明、没有装饰
④StageStyle.UTILITY
a.纯白色背景和最小平台装饰
b.上面只有标题和关闭
① Modality.NONE - 默认
② Modality.APPLICATION_MODAL
全局模态,使用一个stage时,其他stage都是禁用的
③ Modality.WINDOW_MODAL
只禁用父窗口(其中父窗口可以通过 .initOwner(stage);来设置)
这个基本也不用,有时候可能会用关闭事件。
代码演示:
@Override
public void start(Stage stage) throws Exception {
Label label = new Label("hello world!");
BorderPane pane = new BorderPane(label);
// 取消系统默认退出
Platform.setImplicitExit(false);
stage.setOnCloseRequest(event -> {
// 取消关闭窗口的动作
event.consume();
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("退出程序");
alert.setHeaderText(null);
alert.setContentText("您是否要退出程序?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) {
// 退出程序
Platform.exit();
// 如果仅仅是关闭窗口,则可以使用:stage.close()
}
});
Scene scene = new Scene(pane, 300, 300);
stage.setScene(scene);
stage.setTitle("Hello");
stage.getIcons().add(new Image("images/icon1.png"));
stage.setResizable(false);
stage.initStyle(StageStyle.DECORATED);
stage.show();
}
说明:一个Stage只能有一个Scene,但是可以切换
代码示例如下:
@Override
public void start(Stage stage) throws Exception {
// 第一个Scene
Label label1 = new Label("这是Scene1");
label1.setLayoutX(100);
label1.setLayoutY(100);
Button button1 = new Button("切换");
button1.setLayoutX(100);
button1.setLayoutY(200);
AnchorPane pane1 = new AnchorPane();
pane1.getChildren().addAll(label1, button1);
Scene scene1 = new Scene(pane1, 300, 300);
// 可以设置光标图像
scene1.setCursor(new ImageCursor(new Image("images/icon1.png")));
// 第二个Scene
Label label2 = new Label("这是Scene2");
label2.setLayoutX(100);
label2.setLayoutY(100);
Button button2 = new Button("切换回来");
button2.setLayoutX(100);
button2.setLayoutY(200);
AnchorPane pane2 = new AnchorPane();
pane2.getChildren().addAll(label2, button2);
Scene scene2 = new Scene(pane2, 300, 300);
button1.setOnAction(e->{
stage.setScene(scene2);
});
button2.setOnAction(e->{
stage.setScene(scene1);
});
stage.setScene(scene1);
stage.setTitle("按钮Scene测试");
stage.show();
}
抽象的,像label,button都是Node
以label为例:
@Override
public void start(Stage stage) throws Exception {
Label label = new Label("hello world!");
// 设置坐标
label.setLayoutX(100);
label.setLayoutY(100);
// 设置宽高
label.setPrefWidth(100);
label.setPrefHeight(50);
// 设置样式
label.setStyle("-fx-background-color: skyblue; -fx-border-color: #353b40; -fx-border-width: 3px");
// 内容居中
label.setAlignment(Pos.CENTER);
// 设置是否显示,默认true
label.setVisible(true);
// 设置透明度 (0 - 1)
label.setOpacity(0.5);
// 旋转 顺时针旋转的角度
label.setRotate(70);
// 横向或纵向移动
label.setTranslateX(50);
label.setTranslateY(60);
// 缩放
label.setScaleX(1.5);
// 得到是在哪个场景,这里输出为null,可以放到新建Scene后面调用
System.out.println(label.getScene());
AnchorPane root = new AnchorPane();
root.getChildren().addAll(label);
Scene scene = new Scene(root, 300, 300);
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
}
说明:主要使用Property接口
@Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
Scene scene = new Scene(root, 500, 500);
Circle circle = new Circle();
circle.setCenterX(250);
circle.setCenterY(250);
circle.setRadius(100);
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
// 1.属性绑定:单向绑定,将圆心绑定到窗口中间
circle.centerXProperty().bind(scene.widthProperty().divide(2));
circle.centerYProperty().bind(scene.heightProperty().divide(2));
// 2.属性监听器
circle.centerXProperty().addListener(new ChangeListener<Number>() {
// 参数的第一个值其实就是circle.centerXProperty()对象
// 参数第二个值number是旧值 oldNumber
// 参数第三个值t1是变化后的新值 newNumber
@Override
public void changed(ObservableValue<? extends Number> observableValue, Number number, Number t1) {
System.out.println(number);
System.out.println(t1);
System.out.println();
}
});
root.getChildren().addAll(circle);
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
}
1.可以写一个内部类的方式实现这个方法,然后这里直接new那个类
2.也可以写成lambda表达式的形式
3.也可以写成当前的形式
案例1:(点击事件与键盘事件)
public class Test7 extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
Scene scene = new Scene(root, 500, 500);
Label label = new Label("Hello world");
label.setLayoutX(200);
label.setLayoutY(200);
Button button = new Button("向上移动");
button.setLayoutX(300);
button.setLayoutY(200);
root.getChildren().addAll(label, button);
// 给按钮设置一个点击事件
button.setOnAction(new EventHandler<ActionEvent>() {
// 1.可以写一个内部类的方式实现这个方法,然后这里直接new那个类
// 2.也可以写成lambda表达式的形式
// 3.也可以写成当前的形式
@Override
public void handle(ActionEvent actionEvent) {
label.setLayoutY(label.getLayoutY() - 5);
}
});
// 给scene设置一个键盘事件
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent keyEvent) {
KeyCode keyCode = keyEvent.getCode();
if(keyCode.equals(KeyCode.DOWN)){
label.setLayoutY(label.getLayoutY() + 5);
}
}
});
// 再写一次lambda的形式
// scene.setOnKeyReleased(event -> {
// KeyCode keyCode = event.getCode();
// if(keyCode.equals(KeyCode.DOWN)){
// label.setLayoutY(label.getLayoutY() + 5);
// }
// });
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
}
}
@Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
Scene scene = new Scene(root, 500, 500);
TextField textField = new TextField();
textField.setLayoutX(150);
textField.setLayoutY(200);
root.getChildren().add(textField);
// 拖拽移动时,保持接受模式
textField.setOnDragOver(event -> {
event.acceptTransferModes(TransferMode.ANY);
});
// 拖拽停止时,得到文件的绝对路径
textField.setOnDragDropped(event -> {
Dragboard dragboard = event.getDragboard();
if(dragboard.hasFiles()){
String path = dragboard.getFiles().get(0).getAbsolutePath();
textField.setText(path);
}
});
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
}
// 颜色
Color c = Color.BLUE;
c = new Color(0,0,1,1.0);
c = Color.color(0,0,1);
c = Color.color(0,0,1,1.0);
c = Color.rgb(0,0,255);
c = Color.rgb(0,0,255, 1.0);
// 色调(H,hue)在0~360
// 饱和度(S,saturation)在0~100%
// 亮度(B,brightness或V,value)在0~100%
// 如果有第四个参数,就是不透明度,在0~100%
c = Color.hsb(270,1.0,1.0);
c = Color.hsb(270,1.0,1.0, 0.5);
c = Color.web("0x0000FF", 1.0);
c = Color.web("0x0000FF");
c = Color.web("0x00F");
c = Color.web("#00F");
c = Color.web("#0000FF");
c = Color.web("#0000FF", 1.0);
c = Color.web("0000FF", 1.0);
c = Color.web("0000FF");
c = Color.web("rgba(0,0,255,1.0)");
//......
两个构造函数
Font(double size)
Font(String name, double size)
实际使用演示:
// 字体
Label label = new Label("测试字体");
label.setLayoutX(150);
label.setLayoutY(400);
label.setFont(new Font(30));
// 或
label.setFont(Font.font("华文琥珀", FontWeight.BLACK, 30));
// 如果加载不是系统字体,可以先将字体文件放到文件夹中,然后使用loadFont方法
举例:
ImageView imageView = new ImageView();
imageView.setImage(new Image());
root.getChildren().add(imageView);
初始布局,如果用java写
@Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
Scene scene = new Scene(root, 500, 500);
Label label = new Label("Hello world");
label.setLayoutX(150);
label.setLayoutY(200);
label.setFont(new Font(30));
Button button = new Button("向上移动");
button.setLayoutX(150);
button.setLayoutY(260);
// 给按钮设置一个点击事件
button.setOnAction(event -> {
label.setLayoutY(label.getLayoutY() - 5);
});
root.getChildren().addAll(label, button);
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
}
新建一个FXML文件,实现上面同样的布局(改写FXML)
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
prefHeight="400.0" prefWidth="600.0">
<Label text="Hello world" layoutX="150" layoutY="200">
<font>
<Font size="30">Font>
font>
Label>
<Button text="向上移动" layoutX="150" layoutY="260">Button>
AnchorPane>
具体将 启动类、布局、事件 分成三个部分。
@Override
public void start(Stage stage) throws Exception {
// 使用FXMLLoader引入fxml文件
Pane root = FXMLLoader.load(getClass().getResource("demo10.fxml"));
Scene scene = new Scene(root, 500, 500);
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
}
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.huihui.Test10Controller"
prefHeight="400.0" prefWidth="600.0">
<Label fx:id="la" text="Hello world" layoutX="150" layoutY="200">
<font>
<Font size="30">Font>
font>
Label>
<Button fx:id="bu" text="向上移动" layoutX="150" layoutY="260" onAction="#onUp">Button>
AnchorPane>
部分三:
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
public class Test10Controller {
@FXML
Label la;
@FXML
Button bu;
public void onUp() {
la.setLayoutY(la.getLayoutY() - 5);
}
}
官方提供的一个图像化构建工具,具体使用去官网下载相应版本即可。
在 加载完布局文件 以及 Controller类中属性绑定完之后,会自动调用一下这个方法(方法名不要写错,因为是自动调用)
public class Test10Controller {
@FXML
Label la;
@FXML
Button bu;
public void onUp() {
la.setLayoutY(la.getLayoutY() - 5);
}
// 在 加载完布局文件 以及 Controller类中属性绑定完之后,会自动调用一下这个方法
public void initialize(){
System.out.println("......");
}
}
在Application中,要使用如下方式引入fxml文件
然后就可以使用 fxmlLoader.getController();
的形式返回Controller的对象了,就可以调用其中的方法了。
public class Test10 extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
// 使用FXMLLoader引入fxml文件
// Pane root = FXMLLoader.load(getClass().getResource("demo10.fxml"));
// 在此处可以直接控制Controller,需要这种方式引入fxml
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("demo10.fxml"));
Pane root = fxmlLoader.load();
Scene scene = new Scene(root);
// 此时就可以得到Controller了
Test10Controller controller = fxmlLoader.getController();
controller.testApp(scene);
stage.setScene(scene);
stage.setTitle("我是窗口");
stage.show();
}
}
通过传递参数的形式,将scene传递过来,就可以使用它做一些处理。
public class Test10Controller {
@FXML
Label la;
@FXML
Button bu;
public void testApp(Scene scene){
System.out.println("scene");
}
}
说明:
JavaFX只允许 主线程 执行UI更新操作,但是此时带来一个问题,如果主线程去执行一些耗时任务然后更新UI(例如去数据库中查询数据并更新UI上),由于查询耗时,会让用户感到程序很卡。
所以耗时任务还是需要新开一个线程,但是由于javafx不支持多线程更新UI,所以会报错。
此时需要用Platform.runLater(()->{跟新UI操作})
Platform.runLater(()->{
更新UI操作....
});
具体示例如下:
// 点击事件
button.setOnAction(event->{
// 模拟耗时操作,新开一个线程
Thread thread = new Thread(()->{
// 这里可以是查询数据库或者网上返回数据等耗时操作
String res = "辉哥";
// 新进程中,更新UI,需要用Platform.runLater
Platform.runLater(()->{
label.setText(label.getText() + res);
});
});
thread.start();
});
加载网络图片示例演示:
注意: 这个没有调用Platform.runLater,因为监听器本身就自带了这个方法,不需要手动带了
public class Test12 extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
Scene scene = new Scene(root, 500, 500);
ImageView imageView = new ImageView();
imageView.setFitHeight(400);
imageView.setFitWidth(500);
Label label = new Label("图片加载进度为:");
label.setLayoutX(200);
label.setLayoutY(400);
Button button = new Button("获取");
button.setLayoutX(200);
button.setLayoutY(450);
// 点击事件
button.setOnAction(event -> {
// 耗时操作,新开一个线程
Thread thread = new Thread(() -> {
// 这里可以是查询数据库或者网上返回数据等耗时操作
Image image = new Image("http://8.142.121.105:9000/dataset/javafx_pic_load_text.jpg", true);
// 这个没有调用Platform.runLater,因为监听器本身就自带了这个方法,不需要手动带了,带上也没问题
image.progressProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldNumber, Number newNumber) {
int process = (int) (newNumber.doubleValue() * 100);
label.setText("图片加载进度为:" + process + " %");
}
});
imageView.setImage(image);
});
thread.start();
});
root.getChildren().addAll(imageView, label, button);
stage.setScene(scene);
stage.setTitle("按钮测试");
stage.show();
}
}