先ほどの続き
前提条件
- JavaFXのコントロール(ラベルとかボタンとか)はJavaFXのスレッド配下からでないと原則(※1)操作できない(と思う)。
- WebSocketはプログラム本体とは別のスレッドでデータの送受信を行う。
- WebSocketのスレッド内からPlatform#runLaterを呼ぶことで、JavaFXのスレッド配下で行わせたい処理を登録できる。
- 今の実装だと、接続状況の排他制御がきちんとされていないので、「サーバからの切断」と「クライアントからの起動」のシーケンスが重なったときとかにうまく動かないケースがでてくる。(はず)
※1 こちらにあるようにコントロールに結びついたObservableListを別スレッドから操作した場合は、その操作が画面に反映される。ラベルとかボタンとかのコントロールで試した範囲ではJavaFXのスレッドかどうかチェックにかかってうまくいかなかったがやりようはあるかもしれない。
ApplicationクラスとFXML
Applicationクラスは起動するだけ。
public class WSJsonSingle extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("SingleJson.fxml")); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
FXMLはfx:idふって、onActionを紐付けている程度。
<?xml version="1.0" encoding="UTF-8"?> <?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <AnchorPane prefHeight="363.0" prefWidth="502.0" xmlns:fx="http://javafx.com/fxml" fx:controller="jp.co.epea.wsclient.SingleController"> <children> <Label alignment="CENTER" contentDisplay="CENTER" prefHeight="43.0" prefWidth="502.0" text="JSON送受信" /> <VBox layoutY="43.0" prefHeight="331.0" prefWidth="502.0"> <children> <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"> <children> <Button fx:id="startButton" mnemonicParsing="false" onAction="#handleStartButtonAction" text="開始" /> <Button fx:id="stopButton" mnemonicParsing="false" onAction="#handleStoptButtonAction" text="終了" /> <Label fx:id="statusLabel" text="ステータス" /> </children> </HBox> <HBox prefHeight="100.0" prefWidth="200.0"> <children> <TextField fx:id="msgInput" editable="false" prefWidth="200.0" /> <Button fx:id="sendButton" mnemonicParsing="false" onAction="#handleSendAction" text="送信" /> </children> </HBox> <VBox prefHeight="200.0" prefWidth="100.0"> <children> <HBox prefHeight="100.0" prefWidth="200.0"> <children> <Label text="発言者:" /> <Label fx:id="nameLabel" text="発言者初期値(起動時に変更)" /> </children> </HBox> <HBox prefHeight="100.0" prefWidth="200.0"> <children> <Label text="発言日時:" /> <Label fx:id="timeLabel" text="発言日時初期値(起動時に変更)" /> </children> </HBox> <HBox prefHeight="100.0" prefWidth="200.0"> <children> <Label text="発言内容:" /> <Label fx:id="msgLabel" text="発言内容初期値(起動時に変更)" /> </children> </HBox> </children> </VBox> </children> </VBox> </children> </AnchorPane>
起動処理
WSJsonSingle#mainから。
画面項目の初期化と、ClientEndpointのインスタンス化(まだ接続しない)。
public class SingleController implements Initializable {
private WSJsonSingleClient wsClient = null;
@Override
public void initialize(URL location, ResourceBundle resources) {
// コントロールをまとめたクラスを作って
ViewObj obj = new ViewObj(nameLabel, timeLabel, msgLabel, statusLabel,
msgInput, startButton, stopButton, sendButton);
// ClientEndpointに渡してインスタンス化
wsClient = new WSJsonSingleClient(obj);
}
}
public class ViewObj {
private Label nameLabel;
// 略
ViewObj(Label nameLabel, Label timeLabel, Label msgLabel,
Label statusLabel, TextField msgInput, Button startButton,
Button stopButton, Button sendButton) {
super();
// 略
// コントロール類の初期化(項目設定とか、ボタンのdisable周りとか)
this.init();
}
private void init(){
nameLabel.setText("");
// 略
}
}
@ClientEndpoint( decoders = { ClientDecoder.class }, encoders = { ClientEncoder.class })
public class WSJsonSingleClient {
private ViewObj viewObj;
public WSJsonSingleClient(ViewObj viewObj) {
this.viewObj = viewObj;
}
// 略
}
github:https://github.com/epea/test01/tree/WF_JSON_FX_SINGLE