内含一点学习 Java 和 JavaFX 的笔记

前言

这学期有个大作业,是写一个有点类似于消消乐的游戏。写这篇文章算是为了庆祝一下我将近 60 个小时的工作。大概是因为担心查重检测等问题,不允许我们把代码发布到公共的代码仓库,那这里就放个视频吧。 评完分了应该就可以公开 代码 了吧.

一点 JavaFX 的笔记

写 JavaFX 还算是踩了点坑,这里记录下吧。
JavaFX 的 API Document 是非常好的一个地方,每个方法的描述都比较详细。如果想找为了实现某个功能要用到哪个方法,或者是单纯的想要知道这个方法是干什么的话可以选择先翻看这个,能比直接 Google 还省事的。

Listener

这个东西算是我一开始最头疼的东西了。其实上课的时候就不是特别理解,靠着看笔记和照葫芦画瓢算是理解了点皮毛。这里说的 Listener 不是一个 property 带有的 listener(比如 setOnChangeListener), 而是我们自定的,用于在不同的 component 之间进行沟通的。Listener 其实就是以 anonymous class 的形式实现了一个 interface 的 method, 然后把实现出来的这个类注入到了我们要监听的类里面去。我们在被监听类里适宜时 call 被注入进去的 listener.

举个例子来说明下。假设我们有一个 IM,从 WebSocket 这个 class 中 获取到消息。消息分为两类:一类是普通消息,就是单纯的聊天消息;还有一种是指令消息,会对我们 IM 的运行状态进行控制,比如要求在 AppUI 中进行处理后,决定是直接显示在 UI 上,还是交给 AppCore 进行处理。interface NewMsgListener 是我的 listener, 它来负责 AppCoreWebsocket 的交互,所以这两个 class 不需要依赖对方。

NewMsgListener.java

> /**
> * 这是我的 Listener. 它就是单纯的一个 interface, 有且仅有一个方法。
> * 我们可以稍后使用 lambda expression 初始化它. 
> * 当然你可以定义更多方法,但是没啥必要。而且在那种情况下完全可以再创建一个 Listener
> */
> public interface NewMsgListener {
>   public void setOnNewMsg(String msg);
> }
>

WebSocket.java

> /**
> * 这个类负责连接到 ws 服务器获取信息
> */
> public class WebSocket {
>   private ArrayList<NewMsgListener> listeners;
> 
>   public WebSocket() {}
>   public void connect() {}
>   
>   public void addMsgListener(NewMsgListener listenr) {
>       this.listeners.add(listener);
>   }
>   
>   public void msgReceived() {
>       // 设定 `msg` 作为收到的信息
>       for (int i = 0; i < this.listeners.size(); i ++) {
>           this.listeners.setOnNewMsg();
>       }
>   }
> }
>

AppUI.java

> /**
> * 这个类负责连接到 ws 服务器获取信息
> */
> public class AppUI {
>   private final WebSocket ws;
>   public AppUI() {
>       this.ws = new WebSocket();
>       this.ws.connect();
>
>       this.ws.addMsgListener((msg) -> {
>           if (msg.starts("MSG")) {
>               // Do Something    
>           }
>       })
>   }
> }
>

GridPane

我一开始用 GirdPane 的时候不太理解怎么用它来布置布局。我们创建的 GirdPane 一开始都是没有 row 和 column 的限制。往一个 GridPane 里面增加新的 Node 时可以指定它的位置(通过传入这个 Node 的 row 和 column),也可选的限定这个 Node 可以扩展多少 row 和 column(不设定的话默认为 1). 我们也可以通过创建 ColumnConstrains/RowConstrains 然后加入 GridPane 的方式来调账每一行、列的大小。

// 这是我比较喜欢使用的两个增加 node 的方法
newGridPane.add​(Node child, int columnIndex, int rowIndex);
newGridPane.add​(Node child, int columnIndex, int rowIndex, int colspan, int rowspan);

// 这样来限制每个 column 的宽度
ColumnConstraints column1 = new ColumnConstraints();
column1.setPercentWidth(50);
ColumnConstraints column2 = new ColumnConstraints();
column2.setPercentWidth(50);
newGridPane.getColumnConstraints().addAll(column1, column2); // each get 50% of width

在上面这个例子里,我们创建了两个 ColumnConstrains,并且每个都占比 50%. 这样情况下,我们得到的结果就会是只有在 column 0 和 1 的 Node 会被显示出来。我们可以把他们的占比分别调整为 10% 和 20%, 这样还有 70% 的空间留给剩下的 column, 并且不需要创建第三个占比为 70% 的 ColumnConstrains 并增加进去(当然,你让 column 2 占比 70% 的话除外). JavaFX 会自动帮我们分配。

ListProperty 和 ObservableList

就我目前使用到的功能来讲,他们的区别在于,当给 ListProperty 注入 setOnChange listener 后,只要这个 ListProperty 里包含的 child 发生了变动,listener 就会被调用。而这个功能在 ObservableList 中实现,需要整个 list 都被重新赋值。

在 Canvas 上画一个阴影效果

这个是可以通过 for-loop 来实现的。可以先使用针对 Color 的 instance color 使用 .darker 方法得到一个比 color 暗一点的颜色,再用 for-loop 画以这个颜色画出对应的梯形/弧形来。因为怕代码查重的问题,这里就不放例子了。

最后

暂时想不起来别的坑了,日后如果再遇到一些问题,或者是想起来别的值得记录的问题的话,就再加进去。虽然以我的记性,不要指望太多就是了。

除另有声明外,本博客文章均采用 知识共享(Creative Commons) 署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。