JavaFX 初学入门(一):FXML嵌套与原始控件继承

说明

  之前由于做一个小项目需要用swing,结果swing把我折腾的够呛。后来得知有javaFX这个类似于C#中WPF形式的利用XML来写界面的框架之后,马上就转到javaFX上了。找过一些资料但是帮助都不大,最后还是选择直接看官方demo。(我之前是做过android app,有些东西其实都是差不多的。)

  下面选取官方demo中的一个 UnlockCustom 。

  这个demo中涉及到 继承原始控件(自定义),FXML 布局嵌套,以及一些控件动画等。

一、javaFX的入口函数

整个javaFX demo 的入口函数 是 unlock.class  中的main函数。

在这个demo中 main函数中只有一条语句。Application.launch 的作用与swing 中 EventQueue.invokeLater的作用相似,其结果是 启动一个UI线程 并在线程中回调 被加载的类中的start方法。

 1 package unlock;
 2
 3 import java.util.logging.Level;
 4 import java.util.logging.Logger;
 5 import javafx.application.Application;
 6 import javafx.fxml.FXMLLoader;
 7 import javafx.scene.Scene;
 8 import javafx.scene.layout.Pane;
 9 import javafx.stage.Stage;
10
11 /**
12  * Main class for the Unlock Custom sample.
13  * This is boilerplate code:
14  * Loads ‘Unlock.fxml‘, adds the root node to a Scene, and set the scene
15  * to the application primary stage.
16  * <br>In the Unlock Custom demo the key pad is defined as a custom type named Keypad.
17  * From within Unlock.fxml we refer to the key pad by its Java class name, Keypad.
18  */
19 public class Unlock extends Application {
20
21     /**
22      * @param args the command line arguments
23      */
24     public static void main(String[] args) {
25         Application.launch(Unlock.class);
26     }
27
28     @Override
29     public void start(Stage primaryStage) {
30         try {
31             String filename = "Unlock.fxml";
32             Pane page = (Pane) FXMLLoader.load(Unlock.class.getResource(filename));
33             Scene scene = new Scene(page);
34             primaryStage.setScene(scene);
35             primaryStage.setTitle("Unlock Custom Sample");
36             primaryStage.show();
37         } catch (Exception ex) {
38             Logger.getLogger(Unlock.class.getName()).log(Level.SEVERE, null, ex);
39         }
40     }
41 }

unlock.class

在上面的代码中,start方法 加载了一个fxml文件并对其进行处理。被加载的fxml文件经过一系列处理 成为了Pane的引用,随后创建了Scene对象并将pane设置到Scene中,接着将Scene设置到Stage对象中。pane、Scene、Stage是3个窗口容器 与 Swing 中的 Jpanel和JFrame的关系相似。

pane、Scene与Stage具体关系如下图

Stage 与 Swing 中JFrame 一样是顶层容器,构建了整个程序的主题窗口。

Scene 与Swing 中JPanel 一样是中层容器,用于摆放基本控件或其他中下层容器

Pane 与Swing中 Pane 是一样的,都是控件的载体

二、FXML文件说明

FXML文件其实就是一个布局文件,保存控件相关信息,文件结构基本与XML类似。我们可以通过使用Oralce提供的 JavaFX Scene Builder 工具来进行可视化操作,大大的提高了界面设计的效率。

下面是 unlock.class 中加载的FXML 文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2
 3 <?import java.lang.*?>
 4 <?import java.net.*?>
 5 <?import javafx.scene.control.*?>
 6 <?import javafx.scene.image.*?>
 7 <?import javafx.scene.layout.*?>
 8 <?import javafx.scene.shape.*?>
 9 <?import javafx.scene.text.*?>
10 <?import unlock.*?>
11 <?scenebuilder-classpath-element ../../dist/UnlockCustom.jar?>
12
13
14 <AnchorPane id="AnchorPane" fx:id="root" focusTraversable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="unlock.UnlockController">
15   <children>
16     <Text layoutX="76.0" layoutY="230.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Hello World">
17       <font>
18         <Font size="80.0" />
19       </font>
20     </Text>
21     <Rectangle fx:id="okleft" arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="400.0" stroke="BLACK" strokeType="INSIDE" styleClass="unlock-leftright" width="300.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
22     <Rectangle fx:id="okright" arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="400.0" layoutX="300.0" stroke="BLACK" strokeType="INSIDE" styleClass="unlock-leftright" width="300.0" AnchorPane.topAnchor="0.0" />
23     <Rectangle fx:id="error" arcHeight="5.0" arcWidth="5.0" fill="#992500" height="400.0" opacity="0.0" stroke="BLACK" strokeType="INSIDE" width="600.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
24     <Keypad fx:id="pad" layoutX="187.0" layoutY="68.0" styleClass="keypad" />
25     <Rectangle fx:id="unlocktop" arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" styleClass="unlock-top" visible="true" width="600.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
26     <Rectangle fx:id="unlockbottom" arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" layoutY="200.0" stroke="BLACK" strokeType="INSIDE" styleClass="unlock-bottom" visible="true" width="600.0" AnchorPane.leftAnchor="0.0" />
27     <Button fx:id="lock" layoutX="163.0" layoutY="157.0" mnemonicParsing="false" onAction="#unlockPressed" styleClass="unlock-button" text="Click to Unlock" visible="true">
28       <graphic>
29         <ImageView id="lock" pickOnBounds="true">
30           <image>
31             <Image preserveRatio="true" smooth="true" url="@lock.png" />
32           </image>
33         </ImageView>
34       </graphic>
35     </Button>
36   </children>
37   <stylesheets>
38     <URL value="@Unlock.css" />
39   </stylesheets>
40 </AnchorPane>

unlock.fxml

除开xml文件声明的语句,文件前几行是导入控件相应的包,这个操作基本与java的格式相匹配。后面的都是在定义控件并设置相应属性。完成的效果如下:

值得注意的是 几乎很多地方都有 fx:xxxxxx的属性标记,这些是javaFX中已经规定的特殊标记属性。具体内容请查看官方文档

就几个比较重要的 简单的说一下,例如在 控件定义的 第一行中有如下的代码:

<AnchorPane id="AnchorPane" fx:id="root" focusTraversable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="unlock.UnlockController">

其中 id="AnchorPane"  id 是定义文档中节点的id号,需要与 fx:id区分开。节点id在一个scene中是唯一的,它类似于html中的id属性。

  fx:id="root"  fx:id 是定义控件的id号,与android开发中 布局文件里的控件id 一样。可以在.java文件中声明并引用 这个 id 对应的控件,具体方法后面会讲到。

  fx:controller="unlock.UnlockController"  fx:controller 是定义该FXML所对应的控制器类,在这个控制器类中可以处理在fxml中定义的事件响应方法 ,例如 button 点击事件。
下面是个 button控件的定义,除了 fx:id 和其他基本属性的定义以外 还有一个 onAction="#keyPressed" 属性的定义。它就是定了一个事件响应方法名称,值得注意的是方法名前面必须加上# 标识符。

<Button fx:id="del" mnemonicParsing="false" onAction="#keyPressed" text="Del" GridPane.columnIndex="0" GridPane.rowIndex="3" />

三、控制器类

下面再让我们看到前面说的控制器类 UnlockController.class

 1 package unlock;
 2
 3 import java.net.URL;
 4 import java.util.ResourceBundle;
 5 import javafx.event.ActionEvent;
 6 import javafx.fxml.FXML;
 7 import javafx.fxml.Initializable;
 8 import javafx.scene.control.Button;
 9 import javafx.scene.control.PasswordField;
10 import javafx.util.Callback;
11
12 /**
13  * The controller for the custom Keypad component - see ‘Keypad.fxml‘
14  * and ‘Keypad.java‘.
15  */
16 public final class KeypadController implements Initializable {
17
18     @FXML //  fx:id="del"
19     private Button del; // Value injected by FXMLLoader
20
21     @FXML //  fx:id="ok"
22     private Button ok; // Value injected by FXMLLoader
23
24     @FXML //  fx:id="display"
25     private PasswordField display; // Value injected by FXMLLoader
26
27     private Callback<String, Boolean> validateCallback = null;
28
29     // Handler for Button[Button[id=null, styleClass=button]] onAction
30     // Handler for Button[fx:id="del"] onAction
31     // Handler for Button[fx:id="ok"] onAction
32     public void keyPressed(ActionEvent event) {
33         // handle the event here
34         if (event.getTarget() instanceof Button) {
35             if (event.getTarget() == del && !display.getText().isEmpty()) {
36                 delete();
37             } else if (event.getTarget() == ok) {
38                 validateCallback.call(display.getText());
39                 display.setText("");
40             } else if (event.getTarget() != del) {
41                 append(((Button)event.getTarget()).getText());
42             }
43             event.consume();
44         }
45     }
46
47     private void delete() {
48         display.setText(display.getText().substring(0, display.getText().length() -1));
49     }
50
51     private void append(String s) {
52         String text = display.getText();
53         if (text == null) text = "";
54         display.setText(text+s);
55     }
56
57     @Override // This method is called by the FXMLLoader when initialization is complete
58     public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
59         assert del == null : "fx:id=\"del\" was not injected: check your FXML file ‘Keypad.fxml‘.";
60         assert ok != null : "fx:id=\"ok\" was not injected: check your FXML file ‘Keypad.fxml‘.";
61         assert display != null : "fx:id=\"password\" was not injected: check your FXML file ‘Keypad.fxml‘.";
62     }
63
64     void setValidateCallback(Callback<String,Boolean> validateCB) {
65         validateCallback = validateCB;
66     }
67
68 }

KeypadController

在这个类中,我们可知找到  keyPressed 方法,它就是前面 button控件中定义的事件方法。他的处理与android 中 布局文件里 android:onclick 属性类似。剩下的与swing中对事件处理的描述相似。

让我们向前看几行,找到

1     @FXML //  fx:id="del"
2     private Button del; // Value injected by FXMLLoader
3
4     @FXML //  fx:id="ok"
5     private Button ok; // Value injected by FXMLLoader
6
7     @FXML //  fx:id="display"
8     private PasswordField display; // Value injected by FXMLLoader

这里很明确的备注了 几行代码的意图。就是前面我们提到的 fx:id属性 在.java文件中的定义与引用。 想要在java文件中直接引用FXML里定义的控件必须使用@FXML标记来描述待定义的变量,并且变量名要与fxml文件中的fx:id 的值相同。如此一来就可以直接对控件进行操作了。

四、继承原始控件与FXML嵌套处理

让我们在回到 unlock.fxml文件里找到 下面这一行代码

<Keypad fx:id="pad" layoutX="187.0" layoutY="68.0" styleClass="keypad" />

这个是一个自定义控件,keypad 是 Vbox的子类。

 1 public class Keypad extends VBox {
 2
 3     private final KeypadController controller;
 4     public Keypad() {
 5         controller = load();
 6     }
 7
 8     private KeypadController load() {
 9
10         final FXMLLoader loader = new FXMLLoader();
11
12         // fx:root is this node.
13         loader.setRoot(this);
14
15         // The FXMLLoader should use the class loader that loaded
16         // this class (Keypad).
17         loader.setClassLoader(this.getClass().getClassLoader());
18
19         // Keypad.fxml contains the configuration for ‘this‘
20         loader.setLocation(this.getClass().getResource("Keypad.fxml"));
21
22         try {
23             final Object root = loader.load();
24             assert root == this;
25         } catch (IOException ex) {
26             throw new IllegalStateException(ex);
27         }
28
29         final KeypadController keypadController = loader.getController();
30         assert keypadController != null;
31         return keypadController;
32     }
33
34     void setValidateCallback(Callback<String,Boolean> callback) {
35         controller.setValidateCallback(callback);
36     }
37 }

keypad.class

而这个子类在代码中并未声明控件而是加载了一个FXML文件,通过FXML来加载控件。也是这样让我们实现了一个类似于FXML嵌套的处理,所谓FXML的嵌套 实际上就是通过加载自定义控件,在代码中load FXML来完成的。在官方教程中并没有直接说明FXML可以嵌套处理也没有办法,不知道该如何处理。

剩下代码的可以通过官方DEMO自行查看与学习。

JavaFX 初学入门(一):FXML嵌套与原始控件继承

时间: 2024-11-01 15:45:12

JavaFX 初学入门(一):FXML嵌套与原始控件继承的相关文章

(转载)VS2010/MFC编程入门之二十三(常用控件:按钮控件的编程实例)

上一节VS2010/MFC编程入门教程中鸡啄米讲了按钮控件Button.Radio Button和Check Box的基本用法,本节就继续讲按钮控件的内容,通过一个实例让大家更清楚按钮控件在实际的软件开发中如何使用. 因为Button控件在前面的例子中涉及到了,比较简单,本文就不作深入分析了,而是重点讲解单选按钮Radio Button.复选框Check Box的使用. 按钮控件实例的功能 首先介绍此实例实现的功能.此实例用来根据网站类型选择网站,并将选择的网站的名称显示到编辑框中.网站类型有"

scrollview嵌套上下拉控件嵌套recyclerview

相信会碰到很多类似的需求,一个列表控件,然后控件上方的一个头部需要自定义,这样就不好有时候也不能加在列表控件的头部了,那必须得嵌套一层scrollview了,没毛病,那么一般的列表控件都是有上拉下拉的操作,而且一般也是在 github 上找寻一个收藏量高的 来做为一个全局通用的上下拉控件,这里问题就来了,一般的 scrollview 嵌套 recyclerview 或者 listview 都毕竟容易解决,可是在加上一层上下拉控件呢?上下拉控件肯定会有它自己的触摸处理机制,这样你改起来也很麻烦,这

MFC编程入门之二十一(常用控件:编辑框Edit Control)

上一节讲了静态文本框,本节讲的是编辑框(Edit Control)同样是一种很常用的控件,我们可以在编辑框中输入并编辑文本.在前面加法计算器的例子中已经演示了编辑框的基本应用.下面具体讲解编辑框的使用. 编辑框的通知消息 编辑框发生某些事件时会向父窗口发送通知消息.在对话框模板中的编辑框上点击右键,选择"Add Event Handler",为编辑框添加消息处理函数时,可以在"Message type"列表中看到这些消息.下面简单介绍编辑框的部分通知消息. EN_C

【转】VS2010/MFC编程入门之二十(常用控件:静态文本框)

本文转自鸡啄米:http://www.jizhuomi.com/software/177.html 感 谢鸡啄米对本文的贡献. 上一节鸡啄米讲了颜色对话框之后,关于对话框的使用和各种通用对话框的介绍就到此为止了.从本节开始鸡啄米将讲解各种常用控件的用法.常用控件主要包括:静态文本框.编辑框.单选按钮.复选框.分组框.列表框.组合框.图片控件.列表控件.树形控件和进度条控件等等.本节教程先来讲解静态文本框的使用. 控件的通知消息 在将静态文本框的使用之前,先大概讲讲控件的通知消息. 当控件有事件发

05_android入门_GET方式实现登陆(在控件上显示服务端返回的内容)

前台datagrid数据绑定 #region 导出到excel中    /// <summary>    /// 2014-6-6    /// </summary>    /// <param name="sender"></param>    /// <param name="e"></param> protected void lnkbtnExcel_Click(object sender

WPF自学入门(二)WPF-XAML布局控件

上一篇介绍了xaml基本知识,我们已经知道了WPF简单的语法.那么接下来,我们要认识一下WPF的布局容器.布局容器可以使控件按照分类显示,我们一起来看看WPF里面可以使用哪些布局容器用来布局. 在WPF中,布局是由布局容器来完成的,容器里面是可以放控件,容器里面也可以放容器.而在WPF中,布局容器有很多,下面主要介绍最常用的几种布局容器, 下面分别介绍StackPanel,WarpPanel,DockPanel,Grid,Canvas五种布局容器 一.StackPanel 在WPF中StackP

openlayers入门开发系列之地图导航控件篇

本篇的重点内容是利用openlayers来实现了地图导航控件功能,效果图如下: 实现思路如下: 创建一个地图控件基类,除了本篇的地图导航控件继承这个地图控件基类之外,后续的其他地图控件也是继承该基类 地图控件基类中创建地图导航控件函数 地图导航控件类 最后,地图导航控件初始化调用 详细的实现过程见:这里 原文地址:https://www.cnblogs.com/giserhome/p/9550661.html

Zara带你快速入门WPF(4)---菜单与功能区控件

前言:许多数据驱动的应用程序都包含菜单和工具栏或功能区控件,允许用户控制操作,在WPF中,也可以使用功能区控件,所以这里介绍菜单和功能区控件. 一.菜单控件 在WPF中,菜单很容易使用Menu和MenuItem元素创建,如下面代码,其中一个主菜单和一个次菜单,以及一个子菜单项列表. <Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presen

Zara带你快速入门WPF(4)---Command与功能区控件

前言:许多数据驱动的应用程序都包含菜单和工具栏或功能区控件,允许用户控制操作,在WPF中,也可以使用功能区控件,所以这里介绍菜单和功能区控件. 一.菜单控件 在WPF中,菜单很容易使用Menu和MenuItem元素创建,如下面代码,其中一个主菜单和一个次菜单,以及一个子菜单项列表. <Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presen