Javafx 在滚动窗格中使用TilePane的响应式UI设计

Javafx 在滚动窗格中使用TilePane的响应式UI设计,javafx,javafx-2,javafx-8,scrollpane,Javafx,Javafx 2,Javafx 8,Scrollpane,我正试图在Scroll-and-Tilepanes atm机上绞尽脑汁,我遇到了一个问题,如果没有一个肮脏的黑客,我是无法解决的 我有一个有8个平铺的水平平铺面板,我将其设置为有4列,结果是有4个平铺的2行。 我把那块瓷砖放在一个HBox里,因为如果我把它放在一个堆叠窗格里,它会拉伸瓷砖的大小,使我的立柱设置无效。有点奇怪,设置prefColumns/Rows会重新计算TilePane的大小,而不是试图设置实际的列/行数量,这更像是一个肮脏的黑客行为 无论如何,将HBox直接放入ScrollPa

我正试图在Scroll-and-Tilepanes atm机上绞尽脑汁,我遇到了一个问题,如果没有一个肮脏的黑客,我是无法解决的

我有一个有8个平铺的水平平铺面板,我将其设置为有4列,结果是有4个平铺的2行。 我把那块瓷砖放在一个HBox里,因为如果我把它放在一个堆叠窗格里,它会拉伸瓷砖的大小,使我的立柱设置无效。有点奇怪,设置prefColumns/Rows会重新计算TilePane的大小,而不是试图设置实际的列/行数量,这更像是一个肮脏的黑客行为

无论如何,将HBox直接放入ScrollPane也不会起作用,因为即使在第二行分幅被切断后,滚动条也不会出现。在Stackpane中再次设置HBox,然后将其放在滚动窗格中。至少在我将窗口的宽度调整为如此之小之前,瓷砖面板必须重新对齐瓷砖,并显示第三行或更多行

以下是基本程序:

public class Main extends Application {

    @Override
    public void start(Stage stage) {

        TilePane tilePane = new TilePane();
        tilePane.setPadding(new Insets(5));
        tilePane.setVgap(4);
        tilePane.setHgap(4);
        tilePane.setPrefColumns(4);
        tilePane.setStyle("-fx-background-color: lightblue;");

        HBox tiles[] = new HBox[8];
        for (int i = 0; i < 8; i++) {
            tiles[i] = new HBox(new Label("This is node #" + i));
            tiles[i].setStyle("-fx-border-color: black;");
            tiles[i].setPadding(new Insets(50));
            tilePane.getChildren().add(tiles[i]);
        }

        HBox hbox = new HBox();
        hbox.setAlignment(Pos.CENTER);
        hbox.setStyle("-fx-background-color: blue;");
        hbox.getChildren().add(tilePane); 

        StackPane stack = new StackPane();
        stack.getChildren().add(hbox);

        ScrollPane sp = new ScrollPane();
        sp.setFitToHeight(true);
        sp.setFitToWidth(true);
        sp.setContent(stack);

        stage.setScene(new Scene(sp, 800, 600));
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}
public类主扩展应用程序{
@凌驾
公众假期开始(阶段){
TilePane TilePane=新的TilePane();
tilePane.setPadding(新插图(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setpref列(4);
tilePane.setStyle(“-fx背景色:浅蓝色;”);
HBox贴片[]=新HBox[8];
对于(int i=0;i<8;i++){
tiles[i]=新的HBox(新标签(“这是节点#“+i));
平铺[i].setStyle(“-fx边框颜色:黑色;”);
瓷砖[i]。设置填充(新插图(50));
tilePane.getChildren().add(tiles[i]);
}
HBox HBox=新的HBox();
hbox.设置校准(位置中心);
hbox.setStyle(“-fx背景色:蓝色;”);
hbox.getChildren().add(tilePane);
StackPane stack=新的StackPane();
stack.getChildren().add(hbox);
ScrollPane sp=新的ScrollPane();
sp.setFitToHeight(真);
sp.setFitToWidth(真);
sp.setContent(堆栈);
舞台场景(新场景(sp,800600));
stage.show();
}
公共静态void main(字符串[]args){
发射();
}
}
我设法实现了我想要的行为,但这更像是一个肮脏的黑客行为。我在包含TilePane的HBox的高度和宽度中添加了一个侦听器,并假设当高度改变时,由于宽度变小,一列被删除,一个新行被添加。为了做到这一点,我将HBox放在VBox中,这样它就不会随着滚动窗格的高度而增长。对于宽度,我只是简单地计算了是否有空间显示另一个柱(最多4个),以便执行此操作

以下是变化:

public class Main extends Application {

    private boolean notFirstPassHeight;
    private boolean notFirstPassWidth;

    @Override
    public void start(Stage stage) {

        TilePane tilePane = new TilePane();
        tilePane.setPadding(new Insets(5));
        tilePane.setVgap(4);
        tilePane.setHgap(4);
        tilePane.setPrefColumns(4);
        tilePane.setStyle("-fx-background-color: lightblue;");
        // I took the value from ScenicView
        tilePane.prefTileWidthProperty().set(182);

        HBox tiles[] = new HBox[8];
        for (int i = 0; i < 8; i++) {
            tiles[i] = new HBox(new Label("This is node #" + i));
            tiles[i].setStyle("-fx-border-color: black;");
            tiles[i].setPadding(new Insets(50));
            tilePane.getChildren().add(tiles[i]);
        }

        ScrollPane sp = new ScrollPane();
        sp.setFitToHeight(true);
        sp.setFitToWidth(true);

        StackPane stack = new StackPane();

        VBox vbox = new VBox();
        vbox.setStyle("-fx-background-color: red");

        HBox hbox = new HBox();
        hbox.setAlignment(Pos.CENTER);
        hbox.setStyle("-fx-background-color: blue;");
        hbox.getChildren().add(tilePane);

        notFirstPassHeight = false;
        notFirstPassWidth = false;
        hbox.heightProperty().addListener((observable, oldValue, newValue) -> {
            if (oldValue.doubleValue() < newValue.doubleValue() && notFirstPassHeight) {
                tilePane.setPrefColumns(tilePane.getPrefColumns() - 1);
                stack.requestLayout();
            }
            notFirstPassHeight = true;
        });
        hbox.widthProperty().addListener((observable, oldValue, newValue) -> {
             if (oldValue.doubleValue() < newValue.doubleValue() && notFirstPassWidth && tilePane.getPrefColumns() <= 3
                     && (newValue.doubleValue() / (tilePane.getPrefColumns() + 1)) > tilePane.getPrefTileWidth()) {
                 tilePane.setPrefColumns(tilePane.getPrefColumns() + 1);
                 stack.requestLayout();
             }
             notFirstPassWidth = true;
        });

        vbox.getChildren().add(hbox);

        stack.getChildren().add(vbox);

        sp.setContent(stack);
        stage.setScene(new Scene(sp, 800, 600));
        stage.show();
    }

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

}
public类主扩展应用程序{
私有布尔值非第一通行高度;
私有布尔notFirstPassWidth;
@凌驾
公众假期开始(阶段){
TilePane TilePane=新的TilePane();
tilePane.setPadding(新插图(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setpref列(4);
tilePane.setStyle(“-fx背景色:浅蓝色;”);
//我从ScenicView中获取了值
tilePane.prefTileWidthProperty().set(182);
HBox贴片[]=新HBox[8];
对于(int i=0;i<8;i++){
tiles[i]=新的HBox(新标签(“这是节点#“+i));
平铺[i].setStyle(“-fx边框颜色:黑色;”);
瓷砖[i]。设置填充(新插图(50));
tilePane.getChildren().add(tiles[i]);
}
ScrollPane sp=新的ScrollPane();
sp.setFitToHeight(真);
sp.setFitToWidth(真);
StackPane stack=新的StackPane();
VBox VBox=新的VBox();
vbox.setStyle(“-fx背景色:红色”);
HBox HBox=新的HBox();
hbox.设置校准(位置中心);
hbox.setStyle(“-fx背景色:蓝色;”);
hbox.getChildren().add(tilePane);
notFirstPassHeight=false;
notFirstPassWidth=false;
hbox.heightProperty().addListener((可观察、旧值、新值)->{
if(oldValue.doubleValue(){
if(oldValue.doubleValue()
然而,这种方法要求我

1.了解瓷砖面板中瓷砖的宽度

2.考虑我计算中的瓦片填充和间隙是准确的,这在我的例子中不适用。


如果你问我的话,至少这不是一个好方法。对于这样一件基本的事情来说,这个过程太复杂了。必须有一种更好、更简单的方法来实现滚动窗格中TilePane的完全可调整性和所需行为。

TilePane
中设置首选列数和/或行数决定了该平铺窗格的
prefWidth
prefHeight
值的计算。如果要强制最大列数,只需使
maxWidth
等于计算的
prefWidth
:您可以使用

tilePane.setMaxWidth(Region.USE_PREF_SIZE);
这意味着(只要平铺窗格位于管理布局的位置)
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;

public class ScrollingTilePane extends Application {

    @Override
    public void start(Stage stage) {

        TilePane tilePane = new TilePane();
        tilePane.setPadding(new Insets(5));
        tilePane.setVgap(4);
        tilePane.setHgap(4);
        tilePane.setPrefColumns(4);
        tilePane.setStyle("-fx-background-color: lightblue;");

        // dont grow more than the preferred number of columns:
        tilePane.setMaxWidth(Region.USE_PREF_SIZE);

        HBox tiles[] = new HBox[8];
        for (int i = 0; i < 8; i++) {
            tiles[i] = new HBox(new Label("This is node #" + i));
            tiles[i].setStyle("-fx-border-color: black;");
            tiles[i].setPadding(new Insets(50));
            tilePane.getChildren().add(tiles[i]);
        }

        HBox hbox = new HBox();
        hbox.setAlignment(Pos.CENTER);
        hbox.setStyle("-fx-background-color: blue;");
        hbox.getChildren().add(tilePane); 

//        StackPane stack = new StackPane();
//        stack.getChildren().add(tilePane);
//        stack.setStyle("-fx-background-color: blue;");

        ScrollPane sp = new ScrollPane();
//        sp.setFitToHeight(true);
        sp.setFitToWidth(true);
        sp.setContent(hbox);

        stage.setScene(new Scene(sp, 800, 600));
        stage.show();
    }

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