JavaFX简单入门笔记

1.引入javafx

官网: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)引入项目
在这里插入图片描述
在这里插入图片描述

2.报错处理

2.1 报错1

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

2.2 报错2

Exception in Application start method 
java.lang.reflect.InvocationTargetException

在IDEA里面设置一下路径,具体设置方式如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下图该选项输入:

--module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.fxml

在这里插入图片描述

3.相关概念

一个JavaFX应用可以包含多个Stage。
一个Stage只能包含一个Scene,但是Scence可以切换。
在这里插入图片描述

3.1 简单使用

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");
    }
}

4.具体使用

4.1 Stage

  • Title(使用:.setTitle(“Hello”);)
  • icon(使用:.getIcons().add(new Image(“images/icon1.png”));)
  • resiziable(使用:.setResizable(false);)
  • x,y,width,height
  • StageStyle(使用:.initStyle(StageStyle.DECORATED);)
  • Modality(使用:.initModality(Modality.NONE);)
  • event

说明:

  • 1.x,y,width,height一般不用

一般都是设置Scene的大小,让Stage去适应Scene,而不用单独设置自己的宽高

  • 2.StageStyle

四种选择:
StageStyle.DECORATED - 默认
有装饰的纯白背景
StageStyle.UNDECORATED
a.没有装饰的纯白背景
b.没有最上面图标、名字、最小化、最大化、关闭按钮
c.如果没有放Scene,什么都不显示
StageStyle.TRANSPARENT
背景透明、没有装饰
StageStyle.UTILITY
a.纯白色背景和最小平台装饰
b.上面只有标题和关闭

  • 3.Modality

Modality.NONE - 默认

Modality.APPLICATION_MODAL
全局模态,使用一个stage时,其他stage都是禁用的
Modality.WINDOW_MODAL
只禁用父窗口(其中父窗口可以通过 .initOwner(stage);来设置)

  • 4.event

这个基本也不用,有时候可能会用关闭事件。
代码演示:

@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();
}

效果图:
在这里插入图片描述

4.2 Scene

说明:一个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();
}

效果图:
在这里插入图片描述

4.3 Node

抽象的,像label,button都是Node

  • layoutX / layoutY / preWidth / preHeight
  • style / visible / opacity / blendMode
  • tanslateX / tanslateY / ratate / scaleX / scaleY / scaleZ
  • parent / scene / id

以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();
}

效果图:
在这里插入图片描述

4.4 UI控件的属性绑定和属性监听

说明:主要使用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();
}

在这里插入图片描述

4.5 事件驱动编程

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();
    }
}

效果图:
在这里插入图片描述
案例2:(拖拽事件)

@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();
}

在这里插入图片描述
在这里插入图片描述

4.6 Color、Font、Image

  • 1.Color类,代码演示:
// 颜色
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)");
//......
  • 2.Font

两个构造函数

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方法
  • 3.Image

举例:

ImageView imageView = new ImageView();
imageView.setImage(new Image());
root.getChildren().add(imageView);

4.7 FXML布局文件的使用

初始布局,如果用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)

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<?import javafx.scene.text.Font?>
<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();
}
  • 部分二:
    ①上面使用 fx:controller 指定控制类的位置:包名+类名
    ②对于需要操作的地方指定 fx:id
    ③对于按钮,如果像指定事件,使用 onAction=“#方法名” 的形式
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="com.huihui.Test10Controller"
            prefHeight="400.0" prefWidth="600.0">
    <!--  上面使用 fx:controller 指定控制类的位置:包名+类名  -->

    <!--  对于需要操作的地方指定 fx:id  -->
    <!--  对于按钮,如果像指定事件,使用 onAction=“#方法名” 的形式  -->
    <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);
    } 
}

4.8 Scene Builder 构建 fxml布局文件

官方提供的一个图像化构建工具,具体使用去官网下载相应版本即可。

4.9 Controller 中的 initialize方法

在 加载完布局文件 以及 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("......");
    }
}

4.10 Application 里操作 Controller

在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");
    }
}

4.11 JavaFX多线程操作 Platform.runLater

说明:
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();
    }
}

效果图:
在这里插入图片描述

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值