JavaJX学习笔记

it2024-10-25  35

一 安装JavaJX

中文文档: https://www.yiibai.com/javafx

JavaFX 官方文档的位置: https://docs.oracle.com/javase/8/javase-clienttechnologies.htm

自己的开发环境: mac, jdk1.8

自己本来是用的 2020-06版本,但是失败.后续改成 2020-09版本 才可以.

Help -> Install New Software…Add…Name: e(fx)clipse Location: http://download.eclipse.org/efxclipse/updates-released/2.3.0/site/选中出现的2行,然后下一步到最后即可. 然后重启.

二 新建第一个javaJX程序

File -> New -> Project -> JavaFX / JavaFX Project package application; import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.layout.BorderPane; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); Scene scene = new Scene(root,400,400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch(Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } } 出现上述代码, 表示成功.

三 JavaFX 项目结构

默认生成一下文件:

Main.java 主界面application.css 样式build.fxbuild 用于程序的打包发布

1 Main.java里的结构

Application: 应用程序Stage: 舞台(窗口)Scene: 场景Node: 节点

Stage -> Scene -> Pane(Node Tree)


四 常见操作

1 添加标签 Label

Label 标签 用于显示文本

演示: 添加一个 Label控件, 显示一段文本 // 添加文本 Label label = new Label("hello world!"); root.setCenter(label);

完整代码如下

package application; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); // 添加文本 Label label = new Label("hello world!"); root.setCenter(label); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

对象的层级结构

2 添加按钮

添加一个按钮 Button btn = new Button("点我"); root.setCenter(btn); 添加事件处理器类 MyEventHandler (内部类) private class MyEventHandler implements EventHandler<ActionEvent> { @Override public void handle(ActionEvent e) { System.out.println("哎哟!"); } }

注意:

EventHandler 是一个接口(泛型接口, 带类型参数)导包的时候要 import javafx.* 开头的包 设置处理器 将按钮和事件绑定 MyEventHandler handler = new MyEventHandler(); btn.setOnAction(handler);

或者直接写成一行

btn.setOnAction(new MyEventHandler());

上述是 先写自定义类实现接口,然后再绑定. 当我们熟练之后,可以使用 匿名类. btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent arg0) { System.out.println("哎哟!"); } }); 不建议使用 lambda 表达式 lambda表达式降低了可读性!新人请勿使用 lambda表达式!重点:掌握内部类的写法! 当熟练之后再写成匿名类!

完整代码

package application; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); // 添加一个按钮 Button btn = new Button("hello world"); root.setCenter(btn); // 事件绑定处理 方法一 MyEventhandler handler = new MyEventhandler(); // 给按钮绑定事件 btn.setOnAction(handler); /* * 事件绑定处理 方法二 * 上述可以简写为 btn.setOnAction(new MyEventhandler()); * */ /** * 事件绑定处理 方法三 * 匿名类 btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { System.out.println("触发点击事件!"); } }); */ Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } private class MyEventhandler implements EventHandler<ActionEvent> { @Override public void handle(ActionEvent event) { System.out.println("触发点击事件!"); } } public static void main(String[] args) { launch(args); } }

3 添加图片

Image: 用于加载图片文件ImageView: 用于显示图片位置: javafx.scene.image.*

图片的来源

Image 可以用来加载多重渠道的图片,支持bmp, jpg, jpeg, gif, png图片格式.

Image image = new Image(URL)

其中, URL: Uniform Resource Locator , URL可以指向网络图片,资源图片或本地图片.

显示网络图片

Image image = new Image(“https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png”);

完整代码

package application; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); // 添加网络图片 Image image = new Image("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"); ImageView imageView = new ImageView(); imageView.setImage(image); // 图片加入到窗口中 root.setCenter(imageView); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

2 显示本地文件

Image image = new Image( “file:/c:/examples/1.jpg” )

可以这么写:

File f = new File("c:/examples/1.jpg"); String url = f.toURI().toString(); Image image= new Image(url);

简写为:

File f = new File("c:/examples/1.jpg"); Image image= new Image(f.toURI().toString()); 注: 和网络图片比较: 一个是 http: 开头的, 一个是 file: 开头

完整代码

package application; import java.io.File; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); /* * 另外一种写法 * File f = new File("1.jpg"); * String url = f.toURI().toString(); * Image image = new Image(url); * */ Image image = new Image("file:1.jpg"); ImageView imageView = new ImageView(); imageView.setImage(image); root.setCenter(imageView); // 根据图片的宽度高度来设置窗口的宽度高度 double width = image.getWidth(); double height = image.getHeight(); Scene scene = new Scene(root, width, height); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

3 显示资源文件

放在 CLASSPATH 下的文件称为资源文件(与 class 文件一起), 资源文件用包路径定位, 例如

Image image = new Image(“application/2.jpg”);

其中, application 是 package 路径.

注: Eclipse 会自动把 src 下的文件复制到 bin 目录, 程序运行时访问的是 bin 下的文件. (bin在CLASSPATH里)

完整代码

package application; import java.io.File; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); // 资源写法 Image image = new Image("application/2.jpg"); ImageView imageView = new ImageView(); imageView.setImage(image); root.setCenter(imageView); // 根据图片的宽度高度来设置窗口的宽度高度 double width = image.getWidth(); double height = image.getHeight(); Scene scene = new Scene(root, width, height); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

4 相册案例

布局

上面显示按钮 Button,中间显示图片 ImageView

root.setTop(btnOpen); // 上面显示按钮 root.setCenter(imageView); // 中间显示图片

图片数组 // 资源图片 String[] imageUrls = { "application/res/hangmu.jpg", "application/res/tiangong.jpg", "application/res/kongjing500.jpg", }; Image[] images = new Image[3]; ImageView imageView = new ImageView();

方法中,加载图片

for(int i=0; i<images.length; i++) { images[i] = new Image(imageUrls[i]); } 响应按钮点击 // 按钮点击事件的处理 btnOpen.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { showNext(); } }); 循环显示图片 // 显示下个图片 public void showNext() { currentIndex ++; if(currentIndex >=3) currentIndex = 0; imageView.setImage( images[ currentIndex ]); }

完整代码

package application; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { // 资源图片 String[] imageUrls = { "application/res/1.jpg", "application/res/2.jpg", "application/res/3.jpg" }; Image[] images = new Image[3]; ImageView imageView = new ImageView(); int currentIndex = 0; // 当前显示的是哪个图片 public void showNext() { currentIndex++; if (currentIndex >= 3) currentIndex = 0; imageView.setImage(images[currentIndex]); } @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); Button btnOpen = new Button("下一张"); // 按钮点击事件的处理 btnOpen.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { showNext(); } }); // 加载图片 for (int i = 0; i < images.length; i++) { images[i] = new Image(imageUrls[i]); } // 保持比例,适应窗口显示 imageView.setPreserveRatio(true); imageView.setFitWidth(400); imageView.setFitHeight(400); root.setTop(btnOpen); // 上面显示按钮 root.setCenter(imageView); // 中间显示图片 Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); showNext(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

五 布局

1 边缘布局 BorderPane

语法分析

无论是容器还是控件, 都是继承于 Node.

JavaFX 里有几种 Node:

容器, 如 BorderPane, HBox控件, 如 Button, Label形状, 如 Circle, Rectangle ( Text 也是形状 )图表, 如 PieChart, XYChart其他, 如 Canvas, ImageView, MediaView, WebView

示例

package application; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); //顶部 Label label = new Label("大桥"); root.setTop(label); //中央 Image image = new Image("application/bridge.jpg"); ImageView imageView = new ImageView(); imageView.setImage(image); imageView.setPreserveRatio(true); imageView.setFitWidth(400); imageView.setFitHeight(400); root.setCenter(imageView); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

2 水平布局 HBox

语法分析

hbox.getChildren().addAll(…) 不定长度的参数: 可以任意个数的参数

hbox.getChildren().addAll(node1, node2, node3) 相当于 hbox.getChildren().add( node1 ); hbox.getChildren().add( node2 ); hbox.getChildren().add( node3 );

案例的效果和实现代码如下

package application; import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { HBox hbox = new HBox(); // 设置 HBox 竖直居中 hbox.setAlignment(Pos.CENTER); // 设置距离(padding) hbox.setPadding(new Insets(10)); TextField textField = new TextField(); Button selectBtn = new Button("选择文件"); Button uploadBtn = new Button("开始上传"); // 控件加到 HBox中 hbox.getChildren().addAll(textField, selectBtn, uploadBtn); // 让 textField 随着水平方向的增长而增长 HBox.setHgrow(textField, Priority.ALWAYS); // 修改 HBox 的高度 Scene scene = new Scene(hbox, 400, 40); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

3 控件的尺寸

在一个 HBox 中添加3个按钮, 观察它们的显示尺寸

默认的, 每个按钮的尺寸不同 (与文本长度相关)

思考: 如何修改每个按钮的 显示尺寸 ?

控件有3个属性:

最小尺寸: Min最佳尺寸: Pref最大尺寸: Max

同时存在的话, 已最佳尺寸为主. 例如

b1.setMinWidth(10); × b1.setPrefWidth(200); √ 3条同时存在的话, 已最优尺寸为最终样式. b1.setMaxWidth(400); ×

可以分别设置宽度和高度, 例如:

Button b1 = new Button("test"); b1.setMaxWidth(100); b1.setMaxHeight(50); b1.setMaxSize(100, 500);

几个常量

Control.USE_COMPUTED_SIZE (-1.0) 默认值Control.USE_PREF_SIZEDouble.MAX_VALUE

比如: USE_COMPUTED_SIZE 控件能完成显示内容所需要的尺寸,例如, 显示 “中国” 和 “中国人民共和国” 尺寸是不一样的.

完整代码

package application; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Control; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { // 声明 HBox 对象(这里是 根节点) HBox hbox = new HBox(); // 声明3个按钮 Button btn1 = new Button("中国"); Button btn2 = new Button("China"); Button btn3 = new Button("中华人民共和国"); // 设置 btn1 btn1.setMinWidth(10); // 最小宽度 btn1.setPrefWidth(100); // 最优宽度 btn1.setMaxWidth(400); // 最大宽度 // 设置 btn2 btn2.setMinHeight(50); // 最小宽度 // 设置 btn3 用常量的方式 btn3.setMinWidth(Control.USE_COMPUTED_SIZE); btn3.setMinHeight(Control.USE_PREF_SIZE); // 将3个 button 加入到 HBox hbox.getChildren().addAll(btn1, btn2, btn3); Scene scene = new Scene(hbox, 400, 50); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } } 结论: 显示尺寸最终由布局容器决定, min, pref, max 只是参考值.

4 控件的布局

填充 Padding: 子控件(与父级)的边距间距 Spacing: 各个子控件之间的间距对齐 Alignment: 靠左,靠右,居中… 填充方法 setPadding() 方法

hbox.setPadding(new Insets(10)); // 上右下左都是 10. hbox.setPadding(new Insets(10, 10, 30, 40)); // 单独设置 上右下左的距离.

设置间距 setSpacing() 方法

hbox.setSpacing(10); // 每个子控件之间的距离是 10

设置对齐,对齐分为 水平和竖直方向.

hbox.setAlignment(Pos.CENTER); // 这样写是 水平方向和竖直方向都居中对齐.

实现效果

完整代码

package application; import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class Main extends Application { TextField a = new TextField(); Button b = new Button("选择文件"); Button c = new Button("上传"); @Override public void start(Stage primaryStage) { try { HBox hbox = new HBox(); hbox.getChildren().addAll(a, b, c); // 填充 padding, 上下左右都是10px hbox.setPadding(new Insets(10)); // 间距 每个子控件之间的距离 hbox.setSpacing(10); // 对齐 hbox.setAlignment(Pos.CENTER); // 水平方向和竖直方向都是对齐的 Scene scene = new Scene(hbox, 400, 40); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } }

增长优先

HBox.setHgrow( a, Priority.ALWAYS);

实现效果

精确布局

例如, 在 HBox 下有 a,b,c 三个控件

a:固定300pxb: 剩余空间的 50%c: 剩余空间的 50%

这样的布局该如何实现呢?

自定义类继承 HBox类然后定义各个子控件的 大小和坐标. resizeRelocate() 方法 package application; import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.stage.Stage; public class Main extends Application { TextField a = new TextField(); Button b = new Button("选择文件"); Button c = new Button("上传"); @Override public void start(Stage primaryStage) { try { HBox hbox = new MyHBox(); hbox.getChildren().addAll(a, b, c); Scene scene = new Scene(hbox, 500, 40); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } // 派生 HBox class MyHBox extends HBox { protected void layoutChildren() { // 如果完全自己计算布局, 就不需要调用 super super.layoutChildren(); // 自定义布局 double width = getWidth(); double height = getHeight(); // 计算每个控件的宽度 double aa = 300; double bb = (width - aa) * 0.5; double cc = width - aa - bb; // 依次定位: 左上角坐标 x,y/宽,高 double x = 0; a.resizeRelocate(x, 0, aa, height); x += aa; b.resizeRelocate(x, 0, bb, height); x += bb; c.resizeRelocate(x, 0, cc, height); } } public static void main(String[] args) { launch(args); } }

实现效果

5 自定义容器

容器 Pane: 可以容纳多个子节点的窗口面板

现有子类:

AnchorPane, BorderPane, DialogPane, FlowPane, GirdPane, HBox, StackPane, TextFlow, TilePane, VBox…

自定义容器: 派生Pane类, 重写 layoutChildren()

注: Pane.getChildren() 可以获取它的子节点的列表.

6 布局的嵌套

思考: 这样的界面如何实现?

// 顶部 hbox.getChildren().addAll(textField, button); HBox.setHgrow(textField, Priority.ALWAYS); // BorderPane 里嵌套HBox BorderPane root = new BorderPane(); root.setTop(hbox); root.setCenter(textArea);

最终效果

完整代码

package application; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.stage.Stage; public class Main extends Application { HBox hbox = new HBox(); TextField textField = new TextField(); Button button = new Button("添加"); TextArea textArea = new TextArea(); @Override public void start(Stage primaryStage) { try { // 顶部 hbox.getChildren().addAll(textField, button); HBox.setHgrow(textField, Priority.ALWAYS); // BorderPane 里嵌套HBox BorderPane root = new BorderPane(); root.setTop(hbox); root.setCenter(textArea); // 实现点击之后,将 textField的内容加入到 TextArea中 // 按钮点击事件 button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { onAdd(); } }); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public void onAdd() { // 在 TextField 中,取出用户输入的文本 String str = textField.getText(); // 把取到的文本加入到 下面的文本框中 textArea.appendText(str + "\n"); } public static void main(String[] args) { launch(args); } }

六 列表控件

1 列表控件 ListView

JavaFX 有 4种控件来表示多项数据:

ListView: 列表TreeView: 树TableView: 表格TreeTableView: 多列列表

创建ListView步骤

定义数据项

添加一个类,表示列表中每一项对应的数据

static class Student { public int id; public String name; public boolean sex; public Student() { } public Student(int id, String name, boolean sex) { this.id = id; this.name = name; this.sex= sex; } } 数据源

用一个ObservableList来存储原始数据:

ObservableList listData = FXCollections.observableArrayList();

注:ObservableList 是一个比较难懂的设计,初学时只要把它当成普通的ArrayList来用即可。

添加数据 listData.add(new Student(1, "shao", true)); listData.add(new Student(2, "wang", true)); listData.add(new Student(3, "jiang", false)); 创建ListView

创建 ListView时,要指定数组项的类型 。在JavaFX里,泛型被广泛使用,大家要学习习惯它。

ListView listView = new ListView();

设置数据源

listView.setItems( listData );

设置单元格的显示

添加 ListCell 类, ListCell负责列表项里每一个Cell的显示

static class MyListCell extends ListCell<Student> { @Override public void updateItem(Student item, boolean empty) { // FX框架要求必须先调用 super.updateItem() super.updateItem(item, empty); // 自己的代码 if (item == null) { this.setText(""); // 清空显示 } else { this.setText( item.name ); // 显示该项的值 } } }

可以看到,这里也使用了泛型。每一处泛型的参数都是

设置单元格生成器

JavaFX里广泛使用了 Factory 的概念,大致意思是说, 由一个工厂来负责提供每个Cell

listView.setCellFactory(new Callback<ListView<Student>,ListCell<Student>>() { @Override public ListCell<Student> call(ListView<Student> param) { return new MyListCell(); } });

可以看到,call() 方法返回的是一个 ListCell对象。

添加到布局

BorderPane root = new BorderPane(); root.setCenter(listView);

显示效果如图:

完整代码

package application; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import javafx.util.Callback; public class Main extends Application { // 创建 ListView, 指定数据项类型 ListView<Student> listView = new ListView<Student>(); // 数据源 ObservableList<Student> listData = FXCollections.observableArrayList(); @Override public void start(Stage primaryStage) { try { // 准备数据 listData.add(new Student(1, "shao", true)); listData.add(new Student(2, "wang", true)); listData.add(new Student(3, "jiang", false)); // 设置数据源 listView.setItems(listData); // 设置单元格生成器 (工厂) listView.setCellFactory(new Callback<ListView<Student>, ListCell<Student>>() { @Override public ListCell<Student> call(ListView<Student> param) { return new MyListCell(); } }); BorderPane root = new BorderPane(); root.setCenter(listView); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } // 负责单元格Cell的显示 static class MyListCell extends ListCell<Student> { @Override public void updateItem(Student item, boolean empty) { // FX框架要求必须先调用 super.updateItem() super.updateItem(item, empty); // 自己的代码 if (item == null) { this.setText(""); // 清空显示 } else { this.setText(item.name); // 显示该项的值 } } } // 数据项 static class Student { public int id; public String name; public boolean sex; public Student() { } public Student(int id, String name, boolean sex) { this.id = id; this.name = name; this.sex = sex; } } public static void main(String[] args) { launch(args); } }

2 列表项的操作

直接对ObservableList 进行增加、删除、和修改操作,ListView会被通知到并自动更新内容。

增加一项 public void onInsert() { String name = textField.getText(); listData.add(new Student(0, name, true)); } 删除当前选中项 public void onRemove() { int index = listView.getSelectionModel().getSelectedIndex(); //当前选中项 if(index >=0 ) { listData.remove( index ); } } 修改当前选中项 public void onChange() { String name = textField.getText(); int index = listView.getSelectionModel().getSelectedIndex(); if(index >=0 ) { Student s = listData.get(index); s.name = name; listData.set(index, s); } }

实现效果

完整代码

package application; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.stage.Stage; import javafx.util.Callback; public class Main extends Application { // 创建ListView,指定数据项类型 ListView<Student> listView = new ListView<Student>(); // 数据源 ObservableList<Student> listData = FXCollections.observableArrayList(); HBox hbox = new HBox(); TextField textField = new TextField(); Button btnInsert = new Button("增加"); Button btnRemove = new Button("删除"); Button btnChange = new Button("修改"); @Override public void start(Stage primaryStage) { try { // 准备数据 listData.add(new Student(1, "shao", true)); listData.add(new Student(2, "wang", true)); listData.add(new Student(3, "jiang", false)); // 设置数据源 listView.setItems(listData); // 设置单元格生成器(工厂) listView.setCellFactory(new Callback<ListView<Student>, ListCell<Student>>() { @Override public ListCell<Student> call(ListView<Student> param) { return new MyListCell(); } }); BorderPane root = new BorderPane(); root.setCenter(listView); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); hbox.getChildren().addAll(textField, btnInsert, btnRemove, btnChange); HBox.setHgrow(textField, Priority.ALWAYS); root.setTop(hbox); btnInsert.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { onInsert(); } }); btnRemove.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { onRemove(); } }); btnChange.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { onChange(); } }); } catch (Exception e) { e.printStackTrace(); } } public void onInsert() { String name = textField.getText(); listData.add(new Student(0, name, true)); // 里面会通知 ListView } public void onRemove() { int index = listView.getSelectionModel().getSelectedIndex(); if (index >= 0) { listData.remove(index); // 里面会通知 ListView } } public void onChange() { String name = textField.getText(); int index = listView.getSelectionModel().getSelectedIndex(); if (index >= 0) { Student s = listData.get(index); s.name = name; listData.set(index, s); // 里面会通知 ListView } } // 负责单元格 Cell 的显示 static class MyListCell extends ListCell<Student> { @Override protected void updateItem(Student item, boolean empty) { // FX框架要求必须先调用 super.updateItem() super.updateItem(item, empty); // 自己的代码 if (item == null) { this.setText(""); // 清空 } else { this.setText(item.name);// 显示该项的值 } } } // 数据项 static class Student { public int id; public String name; public boolean sex; public Student() { } public Student(int id, String name, boolean sex) { this.id = id; this.name = name; this.sex = sex; } } public static void main(String[] args) { launch(args); } }

3 多项选择

源代码看不懂

4 鼠标事件处理

鼠标事件 MouseEvent

通常在 GUI 开发时, 我们关注的鼠标事件有:

鼠标左键点击(选中)鼠标左键双击(打开)鼠标右键点击(上下文菜单)鼠标拖拽…

在 MouseEvent 对象里, 能得到以下信息:

event.getButton() 按钮(左,中,右)event.getClickCount() 移动(0), 单击(1), 双击(2)event.getX() 点击位置(窗口坐标)event.getScenex() 点击位置(屏幕坐标)

鼠标点击事件的处理

listView.setOnMouseClicked(EventHandler)

5 实战练习-阅读器

七 树控件

1 树控件

树控件, TreeView, 以树状结构来显示数据用法与 ListView 类似

注意:

TreeItem 类用于表示树上的一个节点一个 TreeView 必须有一个根节点TreeItem 可以添加图标 TreeItem.setGraphic()

步骤

数据项, 先定义一个类,表示每一个树节点所对应的数据 private static class ItemData { public String name; // 文件名或目录名 boolean isDir = false; // 是否目录 public ItemData() { } public ItemData(boolean isDir, String name) { this.isDir = isDir; this.name = name; } } 单元格的显示, TreeCell 负责每个树节点的显示 private static class MyTreeCell extends TreeCell<ItemData> { @Override protected void updateItem(ItemData item, boolean empty) { super.updateItem(item, empty); if(item == null) { this.setText(""); } else { this.setText(item.name); } } } 添加节点

向 TreeView 里添加节点

(1)添加根节点

TreeItem item_root = new TreeItem( … ) treeView.setRoot ( item_root )

(2)添加子节点

TreeItem item_c = new TreeItem (…) TreeItem item_java = new TreeItem (…) item_root.getChildren().addAll ( item_c, item_java )

(3)继续添加子节点 …

在每个TreeItem 下又可以添加下一层子节点,最终形成一个树状结构

完整代码

package application; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import javafx.util.Callback; public class Main extends Application { TreeView<ItemData> treeView = new TreeView<ItemData>(); @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); initTreeView(); root.setCenter(treeView); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } // 初始化树 private void initTreeView() { ItemData data_root = new ItemData(true, "啊发你好"); TreeItem<ItemData> item_root = new TreeItem<ItemData>(data_root); ItemData data_c = new ItemData(true, "C/C++学习指南"); TreeItem<ItemData> item_c = new TreeItem<ItemData>(data_c); ItemData data_java = new ItemData(true, "Java学习指南"); TreeItem<ItemData> item_java = new TreeItem<ItemData>(data_java); ItemData data_common = new ItemData(true, "公共基础课程"); TreeItem<ItemData> item_common = new TreeItem<ItemData>(data_common); item_root.getChildren().addAll(item_c, item_java, item_common); // 在"Java学习指南系列" 下添加子节点 if (true) { ItemData data = new ItemData(false, "快速入门"); item_java.getChildren().add(new TreeItem<ItemData>(data)); } if (true) { ItemData data = new ItemData(false, "高级语法"); item_java.getChildren().add(new TreeItem<ItemData>(data)); } // 在"公共基础课程" 下添加子节点 if (true) { ItemData data = new ItemData(false, "CentOS使用教程"); item_common.getChildren().add(new TreeItem<ItemData>(data)); } if (true) { ItemData data = new ItemData(false, "MySQL使用教程"); item_common.getChildren().add(new TreeItem<ItemData>(data)); } // 设置根节点 treeView.setRoot(item_root); // 设置 CellFactory treeView.setCellFactory(new Callback<TreeView<ItemData>, TreeCell<ItemData>>() { @Override public TreeCell<ItemData> call(TreeView<ItemData> param) { return new MyTreeCell(); } }); } // 单元格的使用 private static class MyTreeCell extends TreeCell<ItemData> { @Override protected void updateItem(ItemData item, boolean empty) { super.updateItem(item, empty); if (item == null) { this.setText(""); } else { this.setText(item.name); } } } private static class ItemData { public String name; // 文件名或目录 public boolean isDir = false; // 是否目录 public ItemData() { } public ItemData(boolean isDir, String name) { this.isDir = isDir; this.name = name; } } public static void main(String[] args) { launch(args); } }

2 多列树控件

TreeTableView:

特点:

整体是树状结构支持多列显示基于行进行操作, 每行为一个 TreeItem, 每列显示一个字段.

3 实战练习 - 文件浏览器

八 对话框

1 简单提示框 Alert

什么叫模式对话框(Model)?

showAndWait() 时, 当前输入的焦点在对话框里,后面的界面无法输入.程序卡在 showAndWait() 不往下走, 直到对话框关闭. // 弹窗显示 Alert warning = new Alert(AlertType.WARNING); warning.setHeaderText("出错"); warning.setContentText("用户名错误"); warning.showAndWait();

完整代码

package application; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.PasswordField; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class Main extends Application { // 用户输入框 TextField username = new TextField(); // 密码框 PasswordField password = new PasswordField(); // 登录按钮 Button login = new Button("登录"); @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); VBox vbox = new VBox(); vbox.getChildren().addAll(username, password, login); // 设置子控件的间距 vbox.setSpacing(10); root.setCenter(vbox); // application.css 样式定义 root.setId("root"); username.setPromptText("用户名"); // 提示文字 password.setPromptText("密码"); // 密码 login.setMaxWidth(Double.MAX_VALUE); // 按钮放宽显示 Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); // 登录按钮事件 login.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { doLogin(); } }); } catch (Exception e) { e.printStackTrace(); } } private void doLogin() { String _user = username.getText(); String _pass = password.getText(); // 检查用户名和密码 if (!_user.equals("shaofa") || !_pass.equals("123456")) { System.out.println("用户名/密码错误!"); // 弹窗显示 Alert warning = new Alert(AlertType.WARNING); warning.setHeaderText("出错"); warning.setContentText("用户名错误"); warning.showAndWait(); System.out.println("下一步 ..."); } else { Alert warning = new Alert(AlertType.INFORMATION); warning.setHeaderText("成功"); warning.setContentText("欢迎进入系统!"); warning.showAndWait(); } } public static void main(String[] args) { launch(args); } }

显示效果

2 自定义对话框

除了 Alert 之外, 自带的对话框类还有 TextInputDialog, ChoiceDialog.

示例

package application; import java.util.Optional; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar.ButtonData; import javafx.scene.control.ButtonType; import javafx.scene.control.Dialog; import javafx.scene.control.DialogPane; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class Main extends Application { TextArea textArea = new TextArea(); @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); root.setCenter(textArea); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); // 添加按钮 Button add = new Button("添加"); root.setTop(add); add.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { onAdd(); } }); } catch (Exception e) { e.printStackTrace(); } } private void onAdd() { // 对话框内容 VBox content = new VBox(); TextField t_id = new TextField(); TextField t_name = new TextField(); TextField t_phone = new TextField(); t_id.setPromptText("学号"); t_name.setPromptText("姓名"); t_phone.setPromptText("手机号"); content.setSpacing(10); content.getChildren().addAll(t_id, t_name, t_phone); // Dialog -> DialogPane -> Root Node DialogPane dialogPane = new DialogPane(); dialogPane.setContent(content); // 添加按钮 ButtonType ok = new ButtonType("确定", ButtonData.OK_DONE); dialogPane.getButtonTypes().add(ok); // 创建对话框 Dialog<ButtonType> dlg = new Dialog<ButtonType>(); dlg.setDialogPane(dialogPane); dlg.setTitle("添加学生信息"); // 显示对话框, 并接受返回结果 Optional<ButtonType> result = dlg.showAndWait(); if (result.isPresent() && result.get().getButtonData() == ButtonData.OK_DONE) { int id = Integer.valueOf(t_id.getText()); String name = t_name.getText(); String phone = t_phone.getText(); textArea.appendText("学号: " + id + "\t姓名: " + name + "\t手机号: " + phone + "\n"); } } public static void main(String[] args) { launch(args); } }

显示效果

3 对话框的封装

封装成一个工具类, 可以重复使用

使用场景

该对话框需在多处都有调用该对话框内容比较负责

4 文件对话框

使用 FileChooser 可以打开一个文件选择对话框

以读方式选择一个文件以写方式选择一个文件

区别:

读方式时, 要选择一个已经存在的文件写方式时, 可以选择存在的文件, 也可以新输入一个文件

九 菜单栏

1 菜单栏

菜单栏 MenuBar

2 右键菜单

上下文菜单 Context Menu package application; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.scene.Scene; import javafx.scene.control.ContextMenu; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.MenuItem; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import javafx.util.Callback; public class Main extends Application { // 创建ListView, 指定数据项类型 ListView<Student> listView = new ListView<Student>(); // 数据源 ObservableList<Student> listData = FXCollections.observableArrayList(); // ListView 的上下文菜单 ContextMenu listContextMenu = new ContextMenu(); @Override public void start(Stage primaryStage) { try { // 准备数据 listData.add(new Student(1, "shao", true)); listData.add(new Student(2, "wang", true)); listData.add(new Student(3, "jiang", false)); // 设置数据源 listView.setItems(listData); // 设置单元格生成器(工厂) listView.setCellFactory(new Callback<ListView<Student>, ListCell<Student>>() { @Override public ListCell<Student> call(ListView<Student> param) { return new MyListCell(); } }); initContextMenu(); BorderPane root = new BorderPane(); root.setCenter(listView); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } // 初始化上下文菜单 private void initContextMenu() { MenuItem menuItemRemove = new MenuItem("删除"); menuItemRemove.setOnAction((ActionEvent e) -> { removeItem(); }); // 添加菜单项 listContextMenu.getItems().add(menuItemRemove); // 给ListView设置上下文菜单 listView.setContextMenu(listContextMenu); } // 删除当前选中的项 private void removeItem() { int index = listView.getSelectionModel().getSelectedIndex(); if (index >= 0) { listData.remove(index); } } // 负责单元格Cell的显示 static class MyListCell extends ListCell<Student> { @Override public void updateItem(Student item, boolean empty) { // FX框架要求必须先调用 super.updateItem() super.updateItem(item, empty); // 自己的代码 if (item == null) { this.setText(""); // 清空显示 } else { this.setText(item.name); // 显示该项的值 } } } // 数据项 static class Student { public int id; public String name; public boolean sex; public Student() { } public Student(int id, String name, boolean sex) { this.id = id; this.name = name; this.sex = sex; } } public static void main(String[] args) { launch(args); } }

3 实战练习

十 样式单

1 使用样式单

CSS, Cascading Style Sheets 用于定义界面的显示样式(背景, 边框, 字体等)

-fx-padding: 填充

-fx-font-size: 字体大小

-fx-text-fill: 文字颜色

2 实战练习-列表的样式

最新回复(0)