如何使ListView宽度适合其单元格的宽度

时间:2015-04-26 23:31:23

标签: listview layout javafx

我有一个带自定义单元工厂的ListView。我在单元格minWidth上计算最大值,并将其设置为整个ListView的minWidth。因此,布局中受其他区域约束的ListView会缩小到minWidth。但它所拥有的细胞大小调整为首选宽度,而不是最小。因此,节点不适合包含视口,并显示水平滚动条。

这很讨厌。我想让每个单元格完全适合ListView,并摆脱滚动条。更确切。当ListView减小到最小宽度时,其所有单元格也应减小到这个最小宽度。

我正在寻找一些控制属性来切换一些黑客的单元大小调整策略,以便在没有预定义开关的情况下实现所描述的行为。

更新:添加了图片示例

我创建了SplitPane来简化测试布局。我可以通过用鼠标移动它的分隔符来定义布局限制。

第一个镜头显示了初始问题:不遵守单元节点的实际尺寸(min,max,pref)。 ListView始终使用prefWidth,即使它大于视口宽度。因此,可以避免使用水平滚动。

bad

之后我尝试手动设置首选宽度更小。所以细胞现在完全可见。这不是解决方案。我给出这个镜头来证明单元格具有足够小的minWidth以适合ListView。

ugly

最后一枪也是丑陋的。它留下了可能充满细胞内容的自由空间。这很自然:如果视口大于首选宽度且小于最大宽度,则应该用于调整单元格内容的大小。

可以通过切换到显式指定的静态大小的元素来抑制该问题。但这样做意味着放弃动态布局的所有好处:程序员每次使用CSS应用新皮肤时都必须更改大小常量,并禁止用户调整窗口大小或使用鼠标移动分隔符。

这也是一个糟糕的解决方案。我需要什么 - 使javafx的布局引擎工作。 ListView的最小宽度应该是其单元格的最小最小宽度。 ListView的最大宽度应该是其单元格的最大最大宽度。 ListView的首选宽度应该是其单元格的最大首选宽度。并且ListView的实际宽度应该由其父(本例中的SplitPane)设置,并且与所有三个布局边界相关。之后,这个大小应该传递给细胞。

仅当某些单元格的最小宽度太大而无法放入视口时,才会显示水平滚动条。

1 个答案:

答案 0 :(得分:2)

只需将单元格prefWidthProperty绑定到ListView的widthProperty即可。我为边界等减去了一点。

使用字符串,您将获得省略号...而不是滚动条。

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class CellSize extends Application {

    @Override
    public void start(Stage stage) {
        ObservableList<String> items = FXCollections.observableArrayList(
                "hello world", "This is a test", "this is not a drill","vroom...this IS a drill");

        ListView<String> lv = new ListView(items);
        final VBox leftBox  = new VBox(lv,new Button("Unused"));

        lv.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {

            @Override
            public ListCell<String> call(ListView<String> param) {
                ListCell lc = new ListCell<String>() {

                    @Override
                    protected void updateItem(String item, boolean empty) {
                        super.updateItem(item, empty);
                        if (empty) {
                            setText(null);
                            setGraphic(null);
                        } else {
                            setGraphic(null);
                            if (item == null) setText("null");
                            else {
                                setText(item);
                                double minWidth = 5 * item.length();
                                double maxWidth = 10 * item.length();
                                leftBox.setMinWidth(Math.max(minWidth, leftBox.getMinWidth()));
                                leftBox.setMaxWidth(maxWidth);
                            }
                        }
                    }
                };
                lc.prefWidthProperty().bind(lv.widthProperty().subtract(4));
                return lc;
            }
        });

        SplitPane root = new SplitPane();
        root.getItems().addAll(leftBox,new VBox(new Label("Hello")));
        Scene scene = new Scene(root, 300, 250);

        stage.setScene(scene);
        stage.show();

    }

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

}