JavaFX setHgrow / binding 属性 无限扩展

JavaFX setHgrow / binding property expanding infinitely

这是我的代码:

public class TestPanel extends ScrollPane {

    final int SPACING = 5;
    final int ROW_MAX = 6;

    public TestPanel(ArrayList<Item> items) {

        VBox root = new VBox(); 
        root.setSpacing(SPACING);
        HBox row = null;

        int count = 0;
        for (Item item: items){
            if (count == ROW_MAX || row == null){
                row = new HBox();
                row.setSpacing(SPACING);

                root.getChildren().add(row);
                count = 0;
            }

            CustomBox box = new customBox(item);
            row.getChildren().add(box);

            HBox.setHgrow(box, Priority.ALWAYS);

            //box.prefWidthProperty().bind(row.widthProperty());      // Worked for GridPane

            count++;
        }

        setFitToWidth(true);
        setContent(root);

    }

这是我放置在 V 和 H 框中的自定义框节点元素

public class CustomBox extends StackPane {

    private Items item;
    private Rectangle square;

    private int size = 20; // Irrelevent

    public CustomBox(NewAirbnbListing item) {
        this.item= item;

        setPadding(new Insets(5, 5, 5, 5));

        square = new Rectangle(size, size, Color.RED);


        square.widthProperty().bind(widthProperty());
        //square.heightProperty().bind(heightProperty());


        minHeightProperty().bind(widthProperty()); // Maintains aspect ratio

        getChildren().add(square);

    }

错误:粉色是盒子,灰色是 StackPane 的背景色,用于视觉测试。

我想做的事情:

我想要的:我希望 CustomBox 内的矩形(以及我稍后将添加的其他组件)填充它们所在的 StackPane 的大小,并在 window 调整大小。我基本上希望他们模仿那个窗格的大小。

现在试着解释一下发生了什么,基本上我有一个 class 基本上是一个正方形(稍后会有更多的东西),我想用它来填充我的“网格”。当我第一次这样做时,我使用 GridPane 来扩展我的 class,我还使用注释掉的代码来绑定属性并且它“完美地”工作,它调整了我想要的大小没有任何问题,除了它滞后的事实太疯狂了,因为项目 ArrayList 包含数千个项目,所以这将是一个很长的列表。后来,当我实现存储图像的盒子时,大量的滞后问题就开始了。我的解决方案是用 HBox 和 VBox 组合替换 GridPane,它很好地解决了延迟问题,它还让我可以做一些我不能用 GridPane 做的事情。但是,我链接的 gif 中的问题是我面临的问题。我已经尝试了我能想到的所有绑定属性组合,但它仍然像疯了一样扩展,我只是不知道是什么原因造成的,所以我真的希望这里有人能帮助我。我不太了解 JavaFX,但我是来学习的,非常感谢任何帮助。

我认为您的问题的根本原因在于:square 宽度与 CustomBox 宽度绑定。你可能想知道这是怎么回事。您允许 CustomBox 宽度依赖于它的内容(a.k.a 正方形)并且您的正方形宽度依赖于它的父宽度。因为现在这两个宽度是相互依赖的。宽度呈指数增加。 .

解决此问题的一种可能方法是根据 ScrollPane 视口宽度手动计算 CustomBox 大小。这样你就可以手动控制父级宽度,内容宽度由绑定处理。

TestPanel 中的代码将是:

private DoubleProperty size = new SimpleDoubleProperty();

double padding = 4; // 2px on either side
viewportBoundsProperty().addListener((obs, old, bounds) -> {
    size.setValue((bounds.getWidth() - padding - ((ROW_MAX - 1) * SPACING)) / ROW_MAX);
});

CustomBox box = new CustomBox(item);
box.minWidthProperty().bind(size);

完整的工作演示如下:

import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

public class ScrollPaneContentDemo extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        List<Item> items = new ArrayList<>();
        IntStream.range(1, 1000).forEach(i -> items.add(new Item()));
        TestPanel root = new TestPanel(items);
        Scene scene = new Scene(root, 500, 500);
        stage.setScene(scene);
        stage.setTitle("ScrollPaneContent Demo");
        stage.show();
    }

    class TestPanel extends ScrollPane {
        private final int SPACING = 5;
        private final int ROW_MAX = 6;
        private DoubleProperty size = new SimpleDoubleProperty();

        public TestPanel(List<Item> items) {
            final VBox root = new VBox();
            root.setSpacing(SPACING);
            HBox row = null;
            int count = 0;
            for (Item item : items) {
                if (count == ROW_MAX || row == null) {
                    row = new HBox();
                    row.setSpacing(SPACING);
                    root.getChildren().add(row);
                    count = 0;
                }

                CustomBox box = new CustomBox(item);
                box.minWidthProperty().bind(size);
                row.getChildren().add(box);
                HBox.setHgrow(box, Priority.ALWAYS);
                count++;
            }
            setFitToWidth(true);
            setContent(root);

            double padding = 4;
            viewportBoundsProperty().addListener((obs, old, bounds) -> {
                size.setValue((bounds.getWidth() - padding - ((ROW_MAX - 1) * SPACING)) / ROW_MAX);
            });
        }
    }

    class CustomBox extends StackPane {
        private Item item;
        private Rectangle square;
        private int size = 20;

        public CustomBox(Item item) {
            setStyle("-fx-background-color:#99999950;");
            this.item = item;
            setPadding(new Insets(5, 5, 5, 5));
            square = new Rectangle(size, size, Color.RED);
            square.widthProperty().bind(widthProperty());
            square.heightProperty().bind(heightProperty());

            maxHeightProperty().bind(minWidthProperty());
            maxWidthProperty().bind(minWidthProperty());
            minHeightProperty().bind(minWidthProperty());
            getChildren().add(square);
        }
    }

    class Item {
    }
}