“Blog move” original address: Jane book original publication time: 2017-05-21

One project involved simulating thousands of devices divided into groups of 100 each. Therefore, it is necessary to design a GUI program containing 100 custom controls to simulate the working situation of a group of devices. Selecting the device group through ListView can successfully simulate thousands of devices.

Because Java has a rich third-party library, which is convenient for the low-level implementation of the project, the GUI interface is realized based on the latest Java GUI framework “JavaFX 8”. The application needs to use 100 identical custom controls. You need to use flowpanes to effectively lay out these custom controls.

The final effect is shown in the figure below:

The FlowPane layout panel contains custom controls that are horizontally tiled row by row and automatically wrapped at the edge. Click Item in the left ListView and the custom control group on the right displays the status information for the selected device group. To sum up, the design can achieve the predetermined goal.

1. Basic description of Java FX 8

Use this “JavaFX 8 Tutorial” as a quick introduction to JavaFX 8 for developers with GUI design experience.

1.1 Interface generation Mode

Like most modern GUI development, Java FX 8 has two ways of creating user interfaces:

  • XML file definition
  • Java code creation

In order to be more clear and intuitive, this paper adopts the combination of two interface layout methods.

1.2 Problem Description

Since Java FX 8 is not mainstream, there are many potholes that need to be filled because it is difficult to find a solution on a Chinese website when you encounter a problem, so this article does not explain some of the potholes, but instead lists how to fill them in a separate article.

2. Design the interface root layout

Use “Scene Builder” to open the FXML layout file. In the lower left corner of the picture, fill in a custom Class as the controller Class, activate the control to be acted on, fill in the Class Field at “FX: ID” on the right, and fill in the event handling method below. You can then run View -> Show Sample Controller skeleton to see an example of the code to fill in Controller.

Fill the entry class of the program with the following code:

public class Main extends Application {

    // The entry method is implicitly implemented, so it can be deleted
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        primaryStage.setTitle("bitkyApp");
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("sample.fxml"));
        AnchorPane anchorPane = fxmlLoader.load();           // Set the return type to the layout file root node type
        Controller controller = fxmlLoader.getController();  // Get the Controller class for the layout

        BorderPane rootLayout = FXMLLoader.load(getClass().getResource("rootLayout.fxml"));
        rootLayout.setCenter(anchorPane);
        primaryStage.setScene(new Scene(rootLayout, 800.650)); primaryStage.show(); }}Copy the code

This method is the one in the tutorial. In order to realize the interaction between Controller and external, both the FXML layout file and Controller are coupled in the external class, which is cumbersome. Therefore, custom controls can be used to simplify the external code call.

3. Custom control design

After JavaFX 2, FXML provides fx:root/. In this case, Controller must inherit from FXML node object, and when loading with FXMLLoader, setRoot() method must be called.

Do not specify controllers in FXML. Normally, one FXML can correspond to multiple controllers. For flexibility, we should specify controllers in FXMLLoader.

Based on actual practice, you can use the following methods:

In the Controller panel at the bottom left corner of Scene Builder, check to use fx:root construction and should not fill in the Controller class, as shown in the picture, at this point fx:root/ is the root node, :

The code in the FXML file at this point is for example:

< fx: root XMLNS: fx = "http://javafx.com/fxml/1" type = "TabPane" XMLNS = "http://javafx.com/javafx/8.0.111" >... </fx:root>Copy the code

At this point, the Controller class inherits the type of the root node, such as “TabPane.” Optionally implement the Initializable interface for initialization after control generation, as shown in the following code example:

//Controller class inherits FXML layout file root node type "TabPane"
public class DeviceView extends TabPane implements Initializable {

    public DeviceView(a) {
        loadFxml();
    }

    private void loadFxml(a) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("device_view.fxml"));
        loader.setRoot(this);
        loader.setController(this);
        try {
            loader.load();
        } catch(IOException e) { e.printStackTrace(); }}// This method is executed after the constructor completes
    @Override
    public void initialize(URL location, ResourceBundle resources) {}}Copy the code

At this point, in the Controller corresponding to the main layout, the object of the class “DeviceView” is directly generated as a custom control, and the design purpose can be achieved by adding the object as a child control of FlowPane through the reference of the FlowPane control. The specific code is as follows:

for (int i = 1; i <= 100; i++) {

    // Add a custom control "DeviceView" to the FlowPane
    DeviceView deviceView = new DeviceView(i);
    deviceFlowPane.getChildren().add(deviceView);
	
	// Add a listener for the child control "DeviceView" using observer mode
    deviceView.setListener((status -> {
        if(listener ! =null) listener.btnChanged(status);
    }));
}
Copy the code

4. Use custom controls to simplify the external call code of the main interface

To sum up, the code example of the main interface is as follows:

public class MainView extends BorderPane implements Initializable {

    private static MainView mainView;

    private MainView(a) throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("rootLayout.fxml"));
        loader.setRoot(this);
        loader.setController(this);
        loader.load();
    }

    public static MainView getInstance(a) {
        if (mainView == null) {
            try {
                mainView = new MainView();
            } catch(IOException e) { e.printStackTrace(); }}return mainView;
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {}}Copy the code

This code uses the way of custom controls, and uses the lazy style of singleton mode to facilitate external calls, external call code is as follows:


public class MainLauncher extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
           startApp(primaryStage);
    }

    private void startApp(Stage primaryStage) {
        primaryStage.setTitle("Device simulation client");
        primaryStage.setScene(new Scene(MainView.getInstance()));
        primaryStage.setMaximized(true); primaryStage.show(); }}Copy the code

5. Reference materials

  1. Use the built-in layout panel
  2. JavaFX 8 Tutorial “Chinese”
  3. JavaFX 8 API Document
  4. Gluon Scene Builder
  5. JavaFX creates custom controls