【问题标题】:How to make ListView width fit width of its cells如何使 ListView 宽度适合其单元格的宽度
【发布时间】:2015-07-05 06:22:47
【问题描述】:

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

真恶心。我想让每个单元格完全适合 ListView,并摆脱滚动条。更确切。当 ListView 减小到它的最小宽度时,它的所有单元格也应该减小到这个最小宽度。

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


更新:添加图形示例

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

第一张照片显示了最初的问题:不考虑单元节点的实际尺寸(最小、最大、首选项)。 ListView 总是使用 prefWidth,即使它大于视口宽度。所以在可以避免的情况下使用水平滚动。

之后,我尝试手动将首选宽度设置得更小。所以现在单元格是完全可见的。这不是解决方案。我拍摄这张照片是为了证明单元格的 minWidth 足够小以适合 ListView。

最后一个镜头也很丑。它留下了可能被单元格内容填充的可用空间。这是很自然的:如果视口大于首选宽度且小于最大宽度,则应该使用它来调整单元格内容的大小。

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

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

只有在某些单元格的最小宽度太大而无法放入视口时才应显示水平滚动条。

【问题讨论】:

  • 您能否为您已经尝试过的内容添加代码。如果可能的话,还有一张图片。
  • 我其实是用scala写的。将scala代码发布到javafx问题中是否合适?
  • 添加“scala”标签后是的。尽管可能存在不同的行为,但可以将其转换为 Java。

标签: listview layout javafx


【解决方案1】:

只需将单元格的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);
    }

}

【讨论】:

  • 这会不必要地增加首选宽度。父级(例如 SplitPane)可以使用首选宽度在子级之间公平分配屏幕空间
  • 我将单元格的prefWidth 设置为与ListView 相同的大小,这不会浪费空间,但会使其大小合适,因此没有滚动条,也没有未填充的空间。我注意到您更新了问题,我将编辑示例以添加最小值、最大值和 SplitPane
猜你喜欢
  • 2012-07-01
  • 2013-05-17
  • 1970-01-01
  • 2013-02-20
  • 2012-10-24
  • 1970-01-01
  • 1970-01-01
  • 2014-09-14
  • 1970-01-01
相关资源
最近更新 更多