使用JavaFX开发桌面程序

注:我也是JAVA FX的初学者之一,自己在学习的时候踩了许多的坑,中文英文的资料查了不少,但是觉得FX技术和其他热门技术相比,教程还是太少了。这里就尽量做一点微小的贡献吧

使用环境

注:写这个只是为了说明我的环境,使用和我的不一样的环境在理解这篇教程的时候并没有什么问题,例如使用Windows平台、使用Oracle JDK(这样就不需要再单独安装FX组件了,可以不用MAVEN)、使用Oracle的SceneBuilder。可能唯一一个比较影响体验的就是不使用IDEA而是使用eclipse了

  • Ubuntu18.04LTS
  • OpenJDK 1.8
  • IDEA(with MAVEN):使用MAVEN安装FX环境(OpenJDK不附带FX环境)
  • SceneBuilder(glounhq):这是一个fxml可视化设计环境,使用上不如C#,但起码比纯命令设计强一百倍

搭建JAVA FX环境

  1. 下载IDEA、OpenJDK1.8、SceneBuilder(glounhq).

    SceneBuilder下载地址:https://gluonhq.com/products/scene-builder/#download

  2. 在IDEA中关联SceneBuilder.关联的目的是为了之后可以从IDEA快速打开SceneBuilder来设计页面

    IDEA->File->Settings->Language->Java FX->输入SceneBuilder的路径

    如果是Linux环境,你会发现这个路径还不好找,我是使用locate SceneBuilder命令找到的,路径是:/opt/SceneBuilder/SceneBuilder

  3. 因为OpenJDK没有FX环境,需要我们自己安装。为了便于管理,我们在这里使用MAVEN

    1. 在IDEA中创建一个Java FX项目

    2. 在项目名上右键,选择'Add framework support',选择MAVEN

    3. 在pom.xml文件中加入以下依赖:

      <dependencies>
      <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-controls -->
      <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-controls</artifactId>
      <version>13</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-fxml -->
      <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-fxml</artifactId>
      <version>13</version>
      </dependency>
      </dependencies>

      设计流程

      这里只写一些我已经探索出来的设计流程,如果有不对的请指出~

      1. 先在Resources中创建fxml文件(之所以放在Resources文件夹下,是为了加载的时候方便,之后能看到),创建完成后在文件名上右击,选择'Open in SceneBuilder',之后就可以在SceneBuilder中进行可视化设计了。设计时要注意,对有响应的元素要在code栏下的fx:id中设置id,以便于之后的调用。设计完成后Ctrl+s保存文件

      2. 设计第一个加载的界面。这个可以放在入口的java类的main方法下,举个例子:

        package sample;
        
        import javafx.application.Application;
        import javafx.fxml.FXMLLoader;
        import javafx.scene.Parent;
        import javafx.scene.Scene;
        import javafx.stage.Stage; public class Main extends Application { @Override
        public void start(Stage primaryStage) throws Exception{ Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("Entry.fxml"));//从Resources中获取资源
        primaryStage.setTitle("Course Registration System");
        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.show();
        }
      3. 设计触发器:

        对于每一个Panel,我们要指定一个触发器类,这个是放在该fxml文件中的,例如IDEA中默认创建的就是AnchorPane对象,在它那一行就能找到:fx:controller="sample.MainController",这个MainController就是我创建的一个类

        之后,我们可以对该panel下各个控件设计触发事件后的反映,这个可以在SceneBuilder中填写,在Code那一栏下面。设计了之后,它就会到我们指定的那个触发器类下寻找这个方法,如果没有的话IDEA会提示你创建

        注意,触发器类可以创建多个,这样更便于管理,降低耦合度

      4. 在触发器中获取fxml中的控件对象

        有时候,我们需要在事件相应中获取对象的值,例如设计登录页面时点击'提交'的按钮,我们需要知道输入框的字符串。这时候我们可以在触发器中获取这些元素,前提是我们为这些控件输入了fx:id,它是全局性的,不允许重复。例如我们可以通过声明:

           @FXML
        private TextField username;
        @FXML
        private TextField password;

        获取两个TextField对象下的值:

        usernameString=username.getText();
        passwordString=password.getText();
      5. 页面跳转

        我们需要为每一个页面设计一个Java类,例如我设计了一个SignIn_Student.java:

        package sample;
        
        import javafx.application.Application;
        import javafx.fxml.FXMLLoader;
        import javafx.scene.Parent;
        import javafx.scene.Scene;
        import javafx.stage.Stage;
        public class SignIn_Student extends Application{
        private String usernameString;
        private String passwordString;
        @Override
        public void start(Stage stage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("SignIn_Student.fxml"));//加载页面
        Scene anotherScene=new Scene(root);
        stage.setTitle("Please log in");
        stage.setScene(anotherScene);
        stage.show();
        } }
      6. TableView的使用

        这个控件用起来着实有点麻烦。折腾了好久。

        1. 我们肯定需要在某一个fxml页面中加入了这个TableView,并且输入了Table和它每一个TableColumn的fx:id.

        2. 我们需要为有TableView的fxml文件单独创建一个控制器类,之后会说为什么

        3. 我们需要创建一个类来表示要储存的数据,例如我这里创建了一个Courses.class:(下面的get和set方法是IDEA自动生成的)

          package sample;
          
          import javafx.beans.property.*;
          
          import java.time.LocalDate;
          import java.time.LocalTime; public class Courses { private final StringProperty department;
          private final StringProperty lecturer;
          private final ObjectProperty<LocalDate> Time;
          private final StringProperty location;
          private final IntegerProperty ID; public Courses(String name, String department, String lecturer, LocalDate time, String location, Integer ID) {
          this.name = new SimpleStringProperty(name);
          this.department = new SimpleStringProperty(department);
          this.lecturer = new SimpleStringProperty(lecturer);
          this.Time = new SimpleObjectProperty<LocalDate>(time);
          this.location = new SimpleStringProperty(location);
          this.ID = new SimpleIntegerProperty(ID);
          }
          //String,String,String, Date,String,Integer
          private final StringProperty name; public String getName() {
          return name.get();
          } public StringProperty nameProperty() {
          return name;
          } public void setName(String name) {
          this.name.set(name);
          } public String getDepartment() {
          return department.get();
          } public StringProperty departmentProperty() {
          return department;
          } public void setDepartment(String department) {
          this.department.set(department);
          } public String getLecturer() {
          return lecturer.get();
          } public StringProperty lecturerProperty() {
          return lecturer;
          } public void setLecturer(String lecturer) {
          this.lecturer.set(lecturer);
          } public LocalDate getTime() {
          return Time.get();
          } public ObjectProperty<LocalDate> timeProperty() {
          return Time;
          } public void setTime(LocalDate time) {
          this.Time.set(time);
          } public String getLocation() {
          return location.get();
          } public StringProperty locationProperty() {
          return location;
          } public void setLocation(String location) {
          this.location.set(location);
          } public int getID() {
          return ID.get();
          } public IntegerProperty IDProperty() {
          return ID;
          } public void setID(int ID) {
          this.ID.set(ID);
          }
          }
        4. 我们需要实现的效果是,在加载这个页面时,表格中自动加载数据。填写我们创建的控制器类如下:

          package sample;
          
          import javafx.collections.FXCollections;
          import javafx.collections.ObservableList;
          import javafx.fxml.FXML;
          import javafx.scene.control.TableColumn;
          import javafx.scene.control.TableView;
          import javafx.scene.control.TextField; import java.time.LocalDate; public class MainController {
          @FXML
          private TextField username;
          @FXML
          private TextField password;
          @FXML
          private TableView<Courses> allCoursesTable;
          @FXML
          private TableColumn<Courses,String> CourseNameAttribute;
          @FXML
          private TableColumn<Courses,String> DepartmentAttribute;
          @FXML
          private TableColumn<Courses,String> LectureAttribute;
          @FXML
          private TableColumn<Courses, LocalDate> TimeAttribute;
          @FXML
          private TableColumn<Courses,String> LocationAttribute;
          @FXML
          private TableColumn<Courses,Number> CourseIDAttribute;
          @FXML
          private void initialize() {
          ObservableList<Courses> data= FXCollections.observableArrayList(new Courses("MACHINE LEARNING","COMPUTER","ZHANGYI",LocalDate.of(2012,01,01),"A101",4011));//创建ObservableList对象,将数据装进去
          CourseNameAttribute.setCellValueFactory(cellData->cellData.getValue().nameProperty());
          DepartmentAttribute.setCellValueFactory(cellData->cellData.getValue().departmentProperty());
          LectureAttribute.setCellValueFactory(cellData->cellData.getValue().lecturerProperty());
          TimeAttribute.setCellValueFactory(cellData->cellData.getValue().timeProperty());
          LocationAttribute.setCellValueFactory(cellData->cellData.getValue().locationProperty());
          CourseIDAttribute.setCellValueFactory(cellData->cellData.getValue().IDProperty());
          allCoursesTable.setItems(data);//加载数据
          }
          }

          这就是为什么要用单独的控制器类了,否则initialize方法会在每次创建页面的时候都加载一次,而只有某一个页面有我们说的这些Tabel和Column对象,会报错的。

        5. 写一个方法来跳转到这个页面。

        6. 如何实现页面之间的传参呢?

          对于要传参的页面,我们就不能直接获取parent对象了,而是先要获取FXMLLoader对象:

          FXMLLoader fxmlLoader = new FXMLLoader(getClass().getClassLoader().getResource("MainPanel.fxml"));
          Parent root = fxmlLoader.load();
          MainController mc=fxmlLoader.getController();

          注意这个MainController是我为这个页面写的控制器类

          获取了Controller对象后,我们就可以调用方法,将参数传进去了:

                mc.setPassword(pass);
        mc.setUsername(user);
        mc.handleAllCourses();

        我在MainController这个类中是这样写的:

            public void setUsername(String username){
        usernameString=username;
        }
        public void setPassword(String password){
        passwordString=password;
        }

我画了个流程图来表示传参过程:

  1. Controller的初始化

    java @FXML private void initialize(){ /* 对控制器中注册了的控件进行初始化 */ }

  2. 退出当前页面

     ```java
    Stage stage = (Stage)exitButton.getScene().getWindow();
    stage.close();
    ``` 这里是以一个id为exitButton的Button举例的。
  3. 如何从前向后传递参数?

    意思就是,如果我们通过一个页面的事件进入了一个新页面,如果把在这个页面中得到的参数不借助文件等传递回来呢?当然,既然我们说的是javafx,那么一般就是两个控制器之间的参数传递。一个办法就是,再后一个页面的控制器上定义一个控制器对象和set方法,而前一个页面的控制器对象在创建后一个页面的时候通过set方法将自己传递进去。这样后面的控制器就可以使用前一个控制器的public和protected方法了

  4. 创建监听器

    界面编辑器中内置了一些监听方法,但是这是不够用的,我们还可以自己创建监听器。例如,如何根据音量条的值控制视频音量?

    volumeSD.valueProperty().addListener(new ChangeListener<Number>() {
    @Override
    public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
    mediaPlayer.setVolume(newValue.doubleValue() / 100);
    }
    });

    注意监听器的注册要在页面创建之后、使用之前

    这就是入门的FX教程了,有了这些基本的方法,相信设计一个稍微复杂一点的桌面应用程序已经不是问题了。

使用JavaFX开发桌面程序(一)的更多相关文章

  1. [转载]为什么用Java开发桌面程序的比较少

    这个是本人这几天用javafx开发桌面程序时候想到的,因为虽然javafx比C#之类的确实坑多不少,但是习惯了之后也还行,而且它的一次编译.各处使用真的深得我心.但为什么不流行呢: 作者:bell 来 ...

  2. Java开发桌面程序学习(一)——JavaFx+Jfoenix初始以及搭建

    Java开发桌面程序学习(一)--JavaFx+Jfoenix初始以及搭建 前言 想做一个Java的桌面程序,但是,使用原生的Swing感觉又十分麻烦,那个布局都是拿代码设置,看着十分的乱,偶然的情况 ...

  3. JavaFx开发桌面软件

    JavaFx开发桌面软件 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} Jav ...

  4. Atitit.使用引擎加脚本架构的设计 使用php,js来开发桌面程序。。

    Atitit.使用引擎加脚本架构的设计 使用php,js来开发桌面程序.. 1. 引擎加脚本架构 跨平台,桌面与web的优势1 2. 架构桌面引擎(java,c#)2 3. php桌面引擎要点2 3. ...

  5. node.js 开发桌面程序, 10个令人惊讶的NodeJS开源项目

    用 node-webkit 开源框架. 做企业站,杠杠地 包括电子书和支付宝系统都是node开发的,. 接收传感器发送的数据再运算...对水泵.风机.空调这些硬件进行远程控制. 细数10个令人惊讶的N ...

  6. aardio + Python 可视化快速开发桌面程序,一键生成独立 EXE

    网络上大家分享的 aardio + Python 混合开发的文章很多,不得不说 aardio 与 Python 混合开发是真的简单 !  快速入门 推荐几个快速上手教程:< aardio + P ...

  7. Java开发桌面程序学习(四)——常用应用布局模板和简单分析

    布局 前言 刚开始的时候,不知道使用什么布局,发现SceneBuilder其实有8.5版本的,里面就是有提供一个简单的桌面程序模板,8.5可以去官网下载,不过网速好像有点慢,慢慢等吧,官网下载地址 布 ...

  8. Java开发桌面程序学习(12)——Javafx 悬浮窗提示 tooptip

    Javafx 悬浮窗提示 tooptip 鼠标悬浮在某个控件,弹出提示,效果如下: 代码: //control是某个控件 Tooltip.install(control, new Tooltip(&q ...

  9. Java开发桌面程序学习(11)——javafx 鼠标点击,右击,双击

    javafx 鼠标事件 给某个控件设置鼠标点击监听器,三个条件分别判断为单击,右击还是双击 单击判断 event.getButton()==MouseButton.PRIMARY 右击判断 event ...

随机推荐

  1. phpstorm clone 码云项目到本地 Version Control 不显示

    最近在用码云作为代码仓库,但是建了仓库,也填加了 SSH,把项目利用 phpstorm  VCS --> checkout from version control --> git 克隆到 ...

  2. MySQL-插入更新 ON DUPLICATE KEY UPDATE

    向数据库中插入一条记录,若该数据的主键值(UNIQUE KEY)已经在表中存在,则执行后面的 UPDATE 操作.否则执行前面的 INSERT 操作. 测试表结构 CREATE TABLE `flum ...

  3. buildscript和allprojects的作用和区别是什么?

    在Android Studio的Project的build.gradle中, // Top-level build file where you can add configuration optio ...

  4. ViedoUtil获取视频的缩略图

    package com.jcf.utilsdemo; import android.graphics.Bitmap; import android.media.ThumbnailUtils; publ ...

  5. 【原创smarty仿淘宝商品图片轮播+放大镜效果】

    1.去掉图片集字段,字符串的多余字符 $goods_pic_display=$row[DISPLAY];$goods_pic_display1=str_replace('"', '', $g ...

  6. Heartbeat实现web服务器高可用

    一.Heartbeat概述: Heartbeat的工作原理:heartbeat最核心的包括两个部分,心跳监测部分和资源接管部分,心跳监测可以通过网络链路和串口进行,而且支持冗余链路,它们之间相互发送报 ...

  7. JAVA 基础编程练习题2 【程序 2 输出素数】

    2 [程序 2 输出素数] 题目:判断 101-200 之间有多少个素数,并输出所有素数. 程序分析:判断素数的方法:用一个数分别去除 2 到 sqrt(这个数),如果能被整除,则表明此数不是素数, ...

  8. Python3 Selenium自动化web测试 ==> 第九节 WebDriver高级应用 -- 操作select 和 alert

    学习目的: 掌握页面常规元素的定位方法 场景: 网页正常的select元素下拉框常规方法和select专属方法 正式步骤: step1:常规思路select页面元素定位 处理HTML代码截图 # -* ...

  9. keepalived脑裂问题

    一.对脑裂的理解 在高可用(HA)系统中,当联系2个节点的“心跳线”断开时,本来为一整体.动作协调的HA系统,就分裂成为2个独立的个体.由于相互失去了联系,都以为是对方出了故障.两个节点上的HA软件像 ...

  10. 【并行计算-CUDA开发】有关CUDA当中global memory如何实现合并访问跟内存对齐相关的问题

    ps:这是英伟达二面面的一道相关CUDA的题目.<NVIDIA CUDA编程指南>第57页开始          在合并访问这里,不要跟shared memory的bank conflic ...