29

如果我创建一个 ListView:

new ListView<>(FXCollections.observableArrayList("1", "2", "3"))

我希望它能够创建一个包含 3 行的 ListView。但事实并非如此。它创建了一个大约 17 行的 ListView。有没有办法告诉 ListView 始终是高度,以便始终显示其中的任何项目但没有空白行?

让它自动宽度也很有用,所以它总是和最宽的行一样宽。

这样做的一个目的是它可以在 ScrollPane 中使用。我知道它有自己的滚动条,但它们不能提供足够的控制。

4

10 回答 10

11

我刚刚发现 Paul Marshall 的答案可以使用Bindings.size简化为单行,该 Bindings.size 创建一个表示 ObservableList 大小的数字 jfx 属性:

listView.prefHeightProperty().bind(Bindings.size(itemListProperty).multiply(LIST_CELL_HEIGHT));

遗憾的是,列表单元格高度仍然必须是硬编码的 AFAIK。

于 2014-01-28T14:33:00.327 回答
8

不幸的是,ObservableList 没有一个漂亮、干净的大小属性供我们绑定。相反,通过在列表中添加一个 ListChangeListener 是可行的,至少,我过去就是这样做的。默认情况下,每行的大小应该是 24px,我们需要在 ListView 的边缘的顶部和底部添加一个额外的像素。否则,我们仍然会显示 ListView 的滚动条。首先我们将创建 ObservableList 和 ListView,并设置 ListView 的初始高度:

/*
 * Each row in a ListView should be 24px tall.  Also, we have to add an extra
 * two px to account for the borders of the ListView.
 */
final int ROW_HEIGHT = 24;
final ObservableList items = FXCollections.observableArrayList("1", "2", "3");
final ListView list = new ListView(items);

// This sets the initial height of the ListView:
list.setPrefHeight(items().size() * ROW_HEIGHT + 2);

现在我们必须向 ObservableList 添加一个 ListChangeListener。当列表发生变化时,我们只需更改 ListView 的设置高度以匹配新的行数。如果我们知道我们永远不会从支持 ListView 的 ObservableList 中添加或删除项目,那么我们可以排除监听器。同样,高度是行数乘以每行高度加上边框的两个额外像素:

/*
 * This listener will resize the ListView when items are added or removed
 * from the ObservableList that is backing the ListView:
 */
items.addListener(new ListChangeListener() {
    @Override
    public void onChanger(ListChangeListener.Change change) {
        list.setPrefHeight(items.size() * ROW_HEIGHT + 2);
    }
});

参考: JavaFX ListView 文档JavaFX ObservableList 文档

于 2013-07-03T19:26:45.683 回答
5

您是否正在寻找:

.list-cell:empty {
    -fx-opacity: 0;
}

这将隐藏空单元格。

于 2013-12-29T22:50:46.340 回答
3

我发现了一个相对简单但仍然有点 hacky 的解决方案,它假设所有非空单元格具有相同的高度:而不是解析 css 或类似的,添加一个InvalidationListener到你的listView.getItems(); 当您的项目列表第一次变为非空时,您递归地遍历ListViews 子项,直到找到ListCellwhere的实例!cell.isEmpty(),并存储 的值cell.getHeight()。确保将该代码包装在 a 中Platform.runLater(),以便在ListView布局完成后执行。一旦你有了那个高度,你将它与结果值相乘listView.getItems().size()并调用listView.setMaxHeight(仍在 内部InvalidationListener)。

于 2015-05-20T07:01:40.790 回答
2

StackOverflow 的声誉系统阻止我对 Paul Marshall 的回答发表评论,但我想为其他看到这一点的人补充一点,官方 JavaFX 文档“通常”确认了他对行的 24px 估计 - 请参阅http://上的“fixedCellSize”下载.java.net /jdk8/jfxdocs/javafx/scene/control/ListView.html :

通常单元格约为 24 像素...

因此,虽然我同意“[f] 以某种方式从样式或棘手的东西中找出行高、边框宽度等是混乱的,但可能是唯一的方法”可能是正确的,从一个支持的假设开始by official documentation 是一个很好的起点,并且似乎在我对 ListView 的测试(使用 Java 7)中产生了看起来不错的列表。

于 2013-10-15T08:18:21.550 回答
2

在努力工作并基于@warakawa 答案后,我找到了解决方案。众所周知,Listview的实现让很多人头疼,因为这个我写了两个类来解决“PREF_SIZE”的问题(它有一个常数400的高度和宽度是根据高度计算的)。我编写的皮肤类会按照我们的预期计算大小,并且当新属性“fillWidth”更改为“true”时,还可以防止丑陋的水平条,该属性使单元格尽可能水平地增长。问候。

ListViewFixed.java

package javafxapplication;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.Skin;

public class ListViewFixed<T> extends javafx.scene.control.ListView<T>
{
    // <editor-fold defaultstate="collapsed" desc="Properties">
    private final BooleanProperty fillWidth = new SimpleBooleanProperty(this, "fillWidth");

    public final BooleanProperty fillWidthProperty()
    {
        return fillWidth;
    }

    public final boolean isFillWidth()
    {
        return fillWidth.get();
    }

    public final void setFillWidth(boolean fillWidth)
    {
        this.fillWidth.set(fillWidth);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Methods">
    @Override
    protected Skin createDefaultSkin()
    {
        return new ListViewFixedSkin(this);
    }
    // </editor-fold>
}

ListViewFixedSkin.java

package javafxapplication;

import java.util.Set;
import javafx.beans.Observable;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.IndexedCell;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.Region;

public class ListViewFixedSkin extends com.sun.javafx.scene.control.skin.ListViewSkin
{
    // <editor-fold defaultstate="collapsed" desc="Fields">
    private ListViewFixed listView;
    private ScrollBar scrollBarHorizontal;
    private ScrollBar scrollBarVertical;
    private boolean fillWidthCache;
    private double prefWidthCache;
    private Region placeholderRegion;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Constructors">
    public ListViewFixedSkin(ListViewFixed listView)
    {
        super(listView);

        this.listView = listView;

        registerChangeListener(listView.fillWidthProperty(), "FILL_WIDTH");
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Methods">
    private void updateFillWidth()
    {
        if (scrollBarHorizontal != null && scrollBarVertical != null && fillWidthCache != listView.isFillWidth())
        {
            if (listView.isFillWidth() && !fillWidthCache)
            {
                scrollBarHorizontal.visibleProperty().addListener(this::updateCellsPrefWidth);
                scrollBarVertical.visibleProperty().addListener(this::updateCellsPrefWidth);
            }
            else
            {
                scrollBarHorizontal.visibleProperty().removeListener(this::updateCellsPrefWidth);
                scrollBarVertical.visibleProperty().removeListener(this::updateCellsPrefWidth);
            }

            fillWidthCache = listView.isFillWidth();
        }
    }

    private void updateCellsPrefWidth(Observable o)
    {
        final Insets insets = getSkinnable().getInsets();
        final double prefWidth = getSkinnable().getWidth() + insets.getLeft() + insets.getRight() - scrollBarVertical.getWidth();

        if (prefWidth != prefWidthCache)
        {
            for (int i = 0; i < flow.getCellCount(); i++)
            {
                final IndexedCell cell = flow.getCell(i);

                if (!cell.isEmpty())
                {
                    cell.setPrefWidth(prefWidth);
                }
            }

            prefWidthCache = prefWidth;
        }
    }

    private boolean showingPlaceHolder()
    {
        checkState();

        if (getItemCount() == 0)
        {
            if (placeholderRegion == null)
            {
                updatePlaceholderRegionVisibility();

                final Object obj = getChildren().get(getChildren().size() - 1);
                if (obj instanceof Node && ((Region) obj).getStyleClass().contains("placeholder"))
                {
                    placeholderRegion = (Region) obj;
                }
            }

            if (placeholderRegion != null)
            {
                return true;
            }
        }

        return false;
    }

    @Override
    protected void handleControlPropertyChanged(String p)
    {
        super.handleControlPropertyChanged(p);
        if ("FILL_WIDTH".equals(p))
        {
            updateFillWidth();
        }
    }

    @Override
    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset)
    {
        if (showingPlaceHolder())
        {
            return super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);
        }
        else
        {
            double computedHeight = topInset + bottomInset;

            for (int i = 0; i < flow.getCellCount(); i++)
            {
                final IndexedCell cell = flow.getCell(i);

                if (!cell.isEmpty())
                {
                    computedHeight += cell.getHeight();
                }
            }

            return computedHeight;
        }
    }

    @Override
    protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset)
    {
        double computedWidth = 0;

        if (showingPlaceHolder())
        {
            computedWidth += placeholderRegion.getLayoutBounds().getWidth();
        }
        else
        {
            for (int i = 0; i < flow.getCellCount(); i++)
            {
                final IndexedCell cell = flow.getCell(i);

                if (!cell.isEmpty() && cell.getWidth() > computedWidth)
                {
                    computedWidth = cell.getWidth();
                }
            }

            if (scrollBarVertical != null && scrollBarVertical.isVisible())
            {
                computedWidth += scrollBarVertical.getWidth();
            }
        }

        if (computedWidth != 0)
        {
            return computedWidth + leftInset + rightInset;
        }
        else
        {
            return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);
        }
    }

    @Override
    protected void layoutChildren(double x, double y, double w, double h)
    {
        super.layoutChildren(x, y, w, h);

        if (scrollBarHorizontal == null || scrollBarVertical == null)
        {
            final Set<Node> nodes = getSkinnable().lookupAll(".scroll-bar");

            nodes.stream().forEach((node) ->
            {
                if (node instanceof ScrollBar)
                {
                    final ScrollBar scrollBar = (ScrollBar) node;

                    if (scrollBar.getOrientation() == Orientation.HORIZONTAL)
                    {
                        scrollBarHorizontal = scrollBar;
                    }
                    else
                    {
                        scrollBarVertical = scrollBar;
                    }
                }
            });

            updateFillWidth();
        }
    }

    @Override
    public void dispose()
    {
        if (fillWidthCache)
        {
            scrollBarHorizontal.visibleProperty().removeListener(this::updateCellsPrefWidth);
            scrollBarVertical.visibleProperty().removeListener(this::updateCellsPrefWidth);
        }

        listView = null;

        super.dispose();
    }
    // </editor-fold>
}

警告 ListViewFixedSkin.java 类有一些修复工作要做。在我直接通过引用传递方法的地方,它应该先存储在变量中,然后在添加/附件/删除/分离方法中传递变量。否则会导致内存问题。

于 2016-08-07T00:02:24.077 回答
0

尝试在 ListCell 的子类中设置 PrefHeight(double)。例如在我的代码中

@Override
public void updateItem(File item, boolean empty)
{
  super.updateItem(item, empty);
  if (empty) {
    setText(null);
    setGraphic(null);
  } else {
    setText(item.getName());
    setGraphic(null);
    setPrefHeight(getSize(item)/getsize(folder));
  }
}
于 2014-12-04T09:51:15.653 回答
0

I based my answer on the answers provided by @scribbleslims and @Alexandre Mazari

listView.setPrefHeight(listView.getItems().size() * LIST_CELL_HEIGHT);

You have to define LIST_CELL_HEIGHT on your own.

In my case, I used 29.501.

于 2018-05-17T19:53:27.843 回答
0

设置 ListView 高度的示例图像

如果你有静态列表,可以使用 ListView 类 setPrefHeight 自带的 setter 方法设置列表视图高度

ListView.setPrefHeight(140);
于 2018-04-09T11:36:35.440 回答
0

解决方案是强制 JavaFX 线程在能够计算单元格高度之前完成绘制 ListView 中的项目。

public void loadItems(ObservableList<String> items) {
    Platform.runLater(() -> {
        listView.setItems(items);
        listView.layout();
        listView.applyCss();
                
        double cellHeight = ((LabelCell) listView.lookup(".list-cell")).getHeight();
        double items = listView.getItems().size();
        listView.setPrefHeight(cellHeight * items);
    });
} // loadItems
于 2020-07-25T18:23:10.787 回答