【问题标题】:Certain components stacking on top of each other in JavaFXJavaFX 中的某些组件相互堆叠
【发布时间】:2021-04-24 01:00:55
【问题描述】:

我正在尝试构建一个看起来像这样的nonogram

但目前我的代码会像这样显示线索(顶部和左侧的数字)

当我希望将它们分隔成行时,这些线索会堆叠在一起。

这是我目前使用 JavaFX 的代码:

public class PuzzleView implements FXComponent {

  private Controller controller;
  private Pane board;
  private Pane entirePuzzle;
  private VBox[] vertClues;
  private HBox[] horzClues;
  private Pane[][] tiles;

  public PuzzleView(Controller controller) {
    this.controller = controller;
  }

  @Override
  public Parent render() {
    entirePuzzle = new Pane(); // grid pane
    initBoard();
    initLabels();
    entirePuzzle.getChildren().add(board);
    return entirePuzzle;
  }

  private void initBoard() {
    board = new Pane();
    int width = controller.getClues().getWidth();
    int height = controller.getClues().getHeight();
    board.setLayoutX(100);
    board.setLayoutY(100);
    for (int i=0; i<2; i++) {
      for (int j=0; j<2; j++) {
        Rectangle clues = new Rectangle();
        clues.setWidth(width);
        clues.setHeight(height);
        clues.setLayoutX(i*(width*50));
        clues.setLayoutY(j*height*50);
        clues.setStroke(Color.LIGHTBLUE);
        clues.setStrokeWidth(2);
        clues.setFill(Color.WHITE);
        board.getChildren().add(clues);
      }
    }

    tiles = new Pane[width][height];
    for (int i=0; i<width; i++) {
      for (int j=0; j<height; j++) {
        tiles[i][j] = new Pane();
        Rectangle rect = new Rectangle(50, 50);
        rect.setStroke(Color.LIGHTGRAY);
        rect.setStrokeWidth(1);
        rect.setFill(Color.TRANSPARENT);
        tiles[i][j].getChildren().add(rect);
        tiles[i][j].setLayoutX(i*50);
        tiles[i][j].setLayoutY(j*50);
        board.getChildren().add(tiles[i][j]);
      }
    }
  }

  private void initLabels() {
    int width = controller.getClues().getWidth();
    int height = controller.getClues().getHeight();
    vertClues = new VBox[height];
    horzClues = new HBox[width];
    Clues clue = controller.getClues();
    for (int i = 0; i < width; i++) {
      horzClues[i] = new HBox();
      horzClues[i].setLayoutX(100 + i * width);

      for (int j=0; j<controller.getClues().getColCluesLength(); j++) {
        horzClues[i].setSpacing(width);
        Label label = new Label(String.valueOf(clue.getColClues(i)[j]));
        label.minHeight(25);
        label.minWidth(25);
        horzClues[i].getChildren().add(label);
      }

      entirePuzzle.getChildren().add(horzClues[i]);

    }

    for (int i = 0; i < height; i++) {
      vertClues[i] = new VBox();
      vertClues[i].setLayoutY(100 + i * height);
      for (int j=0; j<controller.getClues().getRowCluesLength(); j++) {
        Label label = new Label(String.valueOf(clue.getRowClues(i)[j]));
        label.minHeight(25);
        label.minWidth(25);
        vertClues[i].getChildren().add(label);
      }

      entirePuzzle.getChildren().add(vertClues[i]);
    }
  }
}

FXComponent 是我创建的一个接口,它只有一个 render() 方法,它返回一个组件对象,我可以将它放在不同的类中。 controller 是我创建的另一个类,它允许我访问拼图本身的属性和其他功能。

【问题讨论】:

  • 您应该在此布局中使用GridPane,不是吗?通过尝试自己手动构建布局,您所做的工作比您需要的要多得多。
  • 当我试图将 entirePuzzle 更改为 GridPane 时,一切都变得更糟,而且到处都是。我尝试阅读有关如何定位东西的文档,但无法将其缠绕在我的头上,它们都只是……有点卡在那里。
  • 我认为与其重新发布您删除的other question,不如简单地编辑原始问题。

标签: java javafx


【解决方案1】:

不是一个完整的答案,只是一个建议,但希望它能帮助你进步。

我建议使用TilePane 来表示每个“图块”都是ToggleButton 的板。请参阅Button color change in javafx 了解更改ToggleButton 的颜色的方法。

将“板”设置为BorderPane 的中心Node,将行提示设置为左侧Node,将列提示设置为顶部Node

请注意,以下代码基于您的other question(您已删除)。

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class Nonogram extends Application {
    private static final int  COLS = 5;

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        root.setCenter(createBoard());
        root.setLeft(createLeftPane());
        root.setTop(createTopPane());
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TilePane createBoard() {
        TilePane board = new TilePane(Orientation.VERTICAL);
        board.setPrefRows(COLS);
        ObservableList<Node> children = board.getChildren();
        for (int row = 0; row < COLS; row++) {
            for (int col = 0; col < COLS; col++) {
                ToggleButton b = new ToggleButton();
                children.add(b);
            }
        }
        return board;
    }

    private Label createColumnLabel(String text) {
        Label label = new Label(text);
        label.setFont(Font.font("Monospaced"));
        label.setWrapText(true);
        label.setMinWidth(17);
        label.setPrefWidth(17);
        label.setMaxWidth(17);
        return label;
    }

    private VBox createLeftPane() {
        VBox leftPane = new VBox(createRowLabel("  3"),
                                 createRowLabel("  2"),
                                 createRowLabel("  2"),
                                 createRowLabel("  1"),
                                 createRowLabel("1 1"));
        return leftPane;
    }

    private Label createRowLabel(String text) {
        Label label = new Label(text);
        label.setFont(Font.font("Monospaced"));
        label.setMinHeight(25);
        label.setPrefHeight(25);
        label.setMaxHeight(25);
        return label;
    }

    private HBox createTopPane() {
        Rectangle rect = new Rectangle(22, 27);
        rect.setFill(Color.AZURE);
        HBox topPane = new HBox(rect,
                                createColumnLabel("1 1"),
                                createColumnLabel("  2"),
                                createColumnLabel("  4"),
                                createColumnLabel("  2"),
                                createColumnLabel("  3"));
        return topPane;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

可能有更好的方法来确定“提示”的大小和对齐方式,但我懒得去寻找它。

这是正在运行的应用程序的屏幕截图。

【讨论】:

  • 这太好了,谢谢!跟进问题..我将如何使用嵌套的 for 循环从我的controller 中打印出线索?我目前有类似this for createTopPane() 的东西,但它们彼此相邻显示,而不是像应该那样分成两行。
  • 基本上,您希望为每个线索创建一个字符串表示形式,以用作Label 的文本。发布一个新问题。提供minimal reproducible example 通过发布一组样本线索并指出您想要的结果是什么,即每个线索的字符串表示。 (如果这个答案很好,也许你会考虑接受它,或者至少投赞成票?)
【解决方案2】:

我认为最好的方法是使用 2x2 GridPane 来保存行线索、列线索和拼图网格,左上部分留空。所有这三个部分将依次拥有自己的 GridPane。所以你在 2x2 屏幕 GridPane 中有 3 个 GridPane。

然后使用行和列约束来控制所有内容的对齐方式,并使拼图 GridPane 成为正方形。我已将其划分为方法,以便数据加载内容与布局部分明显分开。

import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.scene.Scene;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.text.Text;
import javafx.stage.Stage;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Nonogram extends Application {
    private final GridPane puzzleGrid = new GridPane();
    private final GridPane columnClueGrid = new GridPane();
    private final GridPane rowClueGrid = new GridPane();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        GridPane mainGrid = new GridPane();
        mainGrid.add(puzzleGrid, 1, 1);
        mainGrid.add(columnClueGrid, 1, 0);
        mainGrid.add(rowClueGrid, 0, 1);
        configureGrids();
        loadData();
        primaryStage.setScene(new Scene(mainGrid, 300, 270));
        primaryStage.show();
    }


    private void configureGrids() {
        columnClueGrid.getColumnConstraints()
                      .addAll(IntStream.range(0, 8).mapToObj(x -> createColumnConstraints(30)).collect(Collectors.toList()));
        rowClueGrid.getColumnConstraints()
                   .addAll(IntStream.range(0, 3).mapToObj(x -> createColumnConstraints(18)).collect(Collectors.toList()));
        rowClueGrid.getRowConstraints().addAll(IntStream.range(0, 8).mapToObj(x -> new RowConstraints(27)).collect(Collectors.toList()));
        puzzleGrid.getRowConstraints().addAll(IntStream.range(0, 8).mapToObj(x -> new RowConstraints(27)).collect(Collectors.toList()));
        puzzleGrid.getColumnConstraints()
                  .addAll(IntStream.range(0, 8).mapToObj(x -> createColumnConstraints(30)).collect(Collectors.toList()));
        puzzleGrid.gridLinesVisibleProperty().set(true);
    }

    private ColumnConstraints createColumnConstraints(double width) {
        ColumnConstraints results = new ColumnConstraints(width);
        results.setHalignment(HPos.CENTER);
        return results;
    }

    private void loadData() {
        String[][] rowClues =
                {{"1", "1", "1", "2", "", "2", "", ""}, {"1", "1", "1", "2", "1", "1", "", "6"}, {"3", "2", "2", "1", "1", "2", "7", "1"}};
        String[][] columnClues =
                {{"1", "1", "", "2", "", "", "", "1"}, {"2", "1", "1", "1", "1", "4", "3", "1"}, {"1", "3", "3", "2", "3", "2", "3", "3"}};
        for (int col = 0; col < 8; col++) {
            for (int row = 0; row < 3; row++) {
                columnClueGrid.add(new Text(columnClues[row][col]), col, row);
            }
        }
        for (int col = 0; col < 3; col++) {
            for (int row = 0; row < 8; row++) {
                rowClueGrid.add(new Text(rowClues[col][row]), col, row);
            }
        }
        for (int col = 0; col < 8; col++) {
            for (int row = 0; row < 8; row++) {
                puzzleGrid.add(new Text("X"), col, row);
            }
        }
    }
}
 

所以屏幕最终看起来像这样:

在看到带有示例 nonogram 的评论后,我忍不住把它变成了一个完整的工作屏幕。所以我用一个矩形替换了每个正方形中的“X”,并通过单击在 0 和 1 之间切换不透明度:

import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Nonogram extends Application {
    private final GridPane puzzleGrid = new GridPane();
    private final GridPane columnClueGrid = new GridPane();
    private final GridPane rowClueGrid = new GridPane();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        GridPane mainGrid = new GridPane();
        mainGrid.add(puzzleGrid, 1, 1);
        mainGrid.add(columnClueGrid, 1, 0);
        mainGrid.add(rowClueGrid, 0, 1);
        configureGrids();
        for (int col = 0; col < 8; col++) {
            for (int row = 0; row < 8; row++) {
                puzzleGrid.add(createRectangle(), col, row);
            }
        }
        loadData();
        primaryStage.setScene(new Scene(mainGrid, 300, 270));
        primaryStage.show();
    }

    private Node createRectangle() {
        Rectangle rectangle = new Rectangle(26, 23, Color.TEAL);
        rectangle.setOpacity(0);
        rectangle.setOnMouseClicked(evt -> {
            rectangle.setOpacity(1 - rectangle.getOpacity());
        });
        return rectangle;
    }

    private void configureGrids() {
        columnClueGrid.getColumnConstraints()
                      .addAll(IntStream.range(0, 8).mapToObj(x -> createColumnConstraints(30)).collect(Collectors.toList()));
        rowClueGrid.getColumnConstraints()
                   .addAll(IntStream.range(0, 3).mapToObj(x -> createColumnConstraints(18)).collect(Collectors.toList()));
        rowClueGrid.getRowConstraints().addAll(IntStream.range(0, 8).mapToObj(x -> new RowConstraints(27)).collect(Collectors.toList()));
        puzzleGrid.getRowConstraints().addAll(IntStream.range(0, 8).mapToObj(x -> new RowConstraints(27)).collect(Collectors.toList()));
        puzzleGrid.getColumnConstraints()
                  .addAll(IntStream.range(0, 8).mapToObj(x -> createColumnConstraints(30)).collect(Collectors.toList()));
        puzzleGrid.gridLinesVisibleProperty().set(true);
    }

    private ColumnConstraints createColumnConstraints(double width) {
        ColumnConstraints results = new ColumnConstraints(width);
        results.setHalignment(HPos.CENTER);
        return results;
    }

    private void loadData() {
        String[][] rowClues =
                {{"1", "1", "1", "2", "", "2", "", ""}, {"1", "1", "1", "2", "1", "1", "", "6"}, {"3", "2", "2", "1", "1", "2", "7", "1"}};
        String[][] columnClues =
                {{"1", "1", "", "2", "", "", "", "1"}, {"2", "1", "1", "1", "1", "4", "3", "1"}, {"1", "3", "3", "2", "3", "2", "3", "3"}};
        for (int col = 0; col < 8; col++) {
            for (int row = 0; row < 3; row++) {
                columnClueGrid.add(new Text(columnClues[row][col]), col, row);
            }
        }
        for (int col = 0; col < 3; col++) {
            for (int row = 0; row < 8; row++) {
                rowClueGrid.add(new Text(rowClues[col][row]), col, row);
            }
        }
    }
}

所以现在看起来像这样:

【讨论】:

  • 如果我以不同的方式存储线索,程序会如何变化?例如,对于这个例子puzzle,我的行和列线索是像this 这样的商店。
  • 这只是数据处理,这就是我自己拆分 loadData() 的原因,因为它实际上与布局无关。随心所欲地填充行和列,它不会改变 GridPanes 的结构。
【解决方案3】:

看起来它正在按照您的要求做。就像@Zephyr 说的那样,GridPane 布局会为您节省大量手动定位。

您需要以不同的方式设置位置。他们看起来和我一模一样。 然而 从长远来看,它仍然会给您带来问题。如果您最终想要更改窗口大小,布局管理器会为您完成这项工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 2013-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多