JavaFx:处理大量按钮的好习惯是什么

时间:2016-02-01 14:46:41

标签: java javafx

我正在使用JavaFx编写一个项目,它是一个3D-Tic Tac Toe,我有一个3D 4 * 4 * 4页面,我用场景构建器2.0设计并放置了很多按钮作为控件,所以当你点击一个按钮,它的文本变为“R”,当计算机选择它的移动时,它将变为“B”,无论如何这里是我的Controller类的一个示例:

public class Controller {
@FXML
private Button b131;

@FXML
private Button b111;

@FXML
private Button b133;

@FXML
private Button b232;

@FXML
private Button b331;

@FXML
private Button b132;

@FXML
private Button b231;

@FXML
private Button b314;

@FXML
private Button b113;

@FXML
private Button b234;

@FXML
private Button b212;

@FXML
private Button b333;

@FXML
private Button b311;
...
 @FXML
public void Initialize() {
    assert b131 != null : "fx:id=\"b131\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b111 != null : "fx:id=\"b111\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b133 != null : "fx:id=\"b133\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b232 != null : "fx:id=\"b232\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b331 != null : "fx:id=\"b331\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b132 != null : "fx:id=\"b132\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b231 != null : "fx:id=\"b231\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b314 != null : "fx:id=\"b314\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b113 != null : "fx:id=\"b113\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b234 != null : "fx:id=\"b234\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b212 != null : "fx:id=\"b212\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b333 != null : "fx:id=\"b333\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b311 != null : "fx:id=\"b311\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b112 != null : "fx:id=\"b112\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b134 != null : "fx:id=\"b134\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b233 != null : "fx:id=\"b233\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b211 != null : "fx:id=\"b211\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b332 != null : "fx:id=\"b332\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b214 != null : "fx:id=\"b214\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b313 != null : "fx:id=\"b313\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b114 != null : "fx:id=\"b114\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b213 != null : "fx:id=\"b213\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b334 != null : "fx:id=\"b334\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b312 != null : "fx:id=\"b312\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b142 != null : "fx:id=\"b142\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b241 != null : "fx:id=\"b241\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b141 != null : "fx:id=\"b141\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b122 != null : "fx:id=\"b122\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b144 != null : "fx:id=\"b144\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b243 != null : "fx:id=\"b243\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b221 != null : "fx:id=\"b221\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b342 != null : "fx:id=\"b342\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b121 != null : "fx:id=\"b121\" was not injected: check your FXML file 'Scene.fxml'.";
    assert b143 != null : "fx:id=\"b143\" was not injected: check your FXML file 'Scene.fxml'.";
...

我在这个逻辑中设置了按钮fx:id's,'b'代表按钮,第一个整数指向它的页面,第二个和第三个整数代表该按钮的行和列,现在我的问题是事件处理,我编写了一个函数来执行此操作并通过场景构建器的代码部分将其连接到我的按钮,它可以工作:

public void ButtonHandler(){
b111.setOnAction(new EventHandler<javafx.event.ActionEvent>() {
    @Override
    public void handle(javafx.event.ActionEvent event) {
        b111.setText("R");
    }
});
...

但我希望按钮名称有点动态,但我不知道如何有效地做到这一点。 任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:2)

我想我通常建议不要在FXML中创建这样的按钮,而是在控制器代码的循环中创建它们。所以你的FXML文件可以定义布局:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.geometry.Insets?>

<VBox fx:controller="tictactoe3d.GameController" xmlns:fx="http://javafx.com/fxml/1">
    <GridPane fx:id="page0">
        <padding>
            <Insets top="5" left="5" right="5" bottom="5"/>
        </padding>
    </GridPane>
    <GridPane fx:id="page1">
        <padding>
            <Insets top="5" left="5" right="5" bottom="5"/>
        </padding>
    </GridPane>
    <GridPane fx:id="page2">
        <padding>
            <Insets top="5" left="5" right="5" bottom="5"/>
        </padding>
    </GridPane>
    <GridPane fx:id="page3">
        <padding>
            <Insets top="5" left="5" right="5" bottom="5"/>
        </padding>
    </GridPane>
</VBox>

然后你的控制器可以使用明显的for循环简洁地定义按钮:

package tictactoe3d;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;

public class GameController {

    @FXML
    private GridPane page0 ;
    @FXML
    private GridPane page1 ;
    @FXML
    private GridPane page2 ;
    @FXML
    private GridPane page3 ;

    public void initialize() {
        GridPane[] pages = {page0, page1, page2, page3};

        for (int page = 0; page < 4; page++) {
            for (int row = 0; row < 4; row ++) {
                for (int col = 0; col < 4; col++) {
                    Button button = new Button("O");
                    pages[page].add(button, col, row);

                    String message = "Button pressed on page "+page+" row "+row+" column "+col;
                    button.setOnAction(e -> {
                        button.setText("R");
                        // replace with real data update:
                        System.out.println(message);
                    });
                }
            }
        }
    }
}

这是另一种方法,如果出于某种原因你真的想在FXML中定义按钮。您可以通过为单个页面定义FXML文件并将其与Grouping together JavaFX FXML Objects中的技术相结合来缩小问题的规模:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import java.util.ArrayList?>
<?import javafx.geometry.Insets?>

<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="tictactoe3d.PageController">
    <Button fx:id="button00" text="O" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
    <Button fx:id="button01" text="O" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
    <Button fx:id="button02" text="O" GridPane.rowIndex="0" GridPane.columnIndex="2"/>
    <Button fx:id="button03" text="O" GridPane.rowIndex="0" GridPane.columnIndex="3"/>
    <Button fx:id="button10" text="O" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
    <Button fx:id="button11" text="O" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
    <Button fx:id="button12" text="O" GridPane.rowIndex="1" GridPane.columnIndex="2"/>
    <Button fx:id="button13" text="O" GridPane.rowIndex="1" GridPane.columnIndex="3"/>
    <Button fx:id="button20" text="O" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
    <Button fx:id="button21" text="O" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
    <Button fx:id="button22" text="O" GridPane.rowIndex="2" GridPane.columnIndex="2"/>
    <Button fx:id="button23" text="O" GridPane.rowIndex="2" GridPane.columnIndex="3"/>
    <Button fx:id="button30" text="O" GridPane.rowIndex="3" GridPane.columnIndex="0"/>
    <Button fx:id="button31" text="O" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
    <Button fx:id="button32" text="O" GridPane.rowIndex="3" GridPane.columnIndex="2"/>
    <Button fx:id="button33" text="O" GridPane.rowIndex="3" GridPane.columnIndex="3"/>

    <padding>
        <Insets top="5" left="5" bottom="5" right="5"/>
    </padding>

    <fx:define>
        <ArrayList fx:id="buttons">
            <fx:reference source="button00"/>
            <fx:reference source="button01"/>
            <fx:reference source="button02"/>
            <fx:reference source="button03"/>
            <fx:reference source="button10"/>
            <fx:reference source="button11"/>
            <fx:reference source="button12"/>
            <fx:reference source="button13"/>
            <fx:reference source="button20"/>
            <fx:reference source="button21"/>
            <fx:reference source="button22"/>
            <fx:reference source="button23"/>
            <fx:reference source="button30"/>
            <fx:reference source="button31"/>
            <fx:reference source="button32"/>
            <fx:reference source="button33"/>
        </ArrayList>
    </fx:define>
</GridPane>

让控制器在循环中添加处理程序,并为“页面”定义字段:

package tictactoe3d;

import java.util.List;

import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class PageController {

    @FXML
    private List<Button> buttons ;

    private int page ;

    public void setPage(int page) {
        this.page = page ;
    }

    public void initialize() {
        for (int i = 0; i < buttons.size(); i++) {
            Button button = buttons.get(i);
            int row = i % 4 ;
            int column = i / 4 ;
            button.setOnAction(e -> {
                button.setText("R");
                // replace with real data update:
                System.out.println("Button pressed on page "+page+" row "+row+" column "+column);
            });
        }
    }

}

现在使用<fx:include>

将页面组装到另一个FXML文件中
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>

<VBox fx:controller="tictactoe3d.GameController" xmlns:fx="http://javafx.com/fxml/1">
    <fx:include fx:id="page0" source="page.fxml"/>
    <fx:include fx:id="page1" source="page.fxml"/>
    <fx:include fx:id="page2" source="page.fxml"/>
    <fx:include fx:id="page3" source="page.fxml"/>
</VBox>

最后,您的整体布局控制器可以设置各个控制器的页面字段,如下所示:

package tictactoe3d;

import javafx.fxml.FXML;

public class GameController {
    @FXML
    private PageController page0Controller ;
    @FXML
    private PageController page1Controller ;
    @FXML
    private PageController page2Controller ;
    @FXML
    private PageController page3Controller ;

    public void initialize() {
        page0Controller.setPage(0);
        page1Controller.setPage(1);
        page2Controller.setPage(2);
        page3Controller.setPage(3);
    }
}

你甚至可以更进一步,用四个按钮定义一个“行”FXML,并使页面包含四个副本(而不是16个按钮)。这会阻止你使用GridPane;您必须使用包含VBox es的HBox,这可能会使得很难将所有内容保存在网格中。