How do you get JavaFX ListView to be the height of

2019-04-18 13:10发布

If I create a ListView:

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

I would expect it to create a ListView with 3 rows. But it doesn't. It creates a ListView of 17 or so rows. Is there a way to tell ListView to always be the height such that whatever items are in it are always shown but no blank rows?

Having it auto width would also be useful, so it's always as wide as the widest row.

One purpose of this is that then it could be used in a ScrollPane. I know it has its own scrollbars, but they don't offer sufficient control.

标签: javafx-2
9条回答
对你真心纯属浪费
2楼-- · 2019-04-18 13:37

StackOverflow's reputation system is preventing me from commenting on Paul Marshall's answer, but I wanted to add for anyone else looking at this that his 24px estimate for rows is "generally" confirmed by the official JavaFX documentation - see "fixedCellSize" at http://download.java.net/jdk8/jfxdocs/javafx/scene/control/ListView.html:

Generally cells are around 24px...

So while I agree that "[f]iguring out row height, border width, etc., somehow from styling or tricky stuff is messy, but probably the only way to do it" may be true, starting with an assumption that is backed up by official documentation is a good place to start, and seems to result in decent-looking lists in my testing on a ListView (using Java 7).

查看更多
Ridiculous、
3楼-- · 2019-04-18 13:38

I just found out that Paul Marshall's answer can be reducted to a one-liner using Bindings.size that creates a numeric jfx property representing the size of an ObservableList :

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

The list cell height must sadly still be hardcoded AFAIK.

查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-04-18 13:40

Try to setPrefHeight(double) in subclass of ListCell. For example in my code

@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));
  }
}
查看更多
看我几分像从前
5楼-- · 2019-04-18 13:42

Are you searching for that:

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

This will hide empty cell's.

查看更多
Lonely孤独者°
6楼-- · 2019-04-18 13:43

Example Image to set ListView Height

If you have a static list , You can set the list view height with the setter method that comes with the ListView class setPrefHeight

ListView.setPrefHeight(140);
查看更多
倾城 Initia
7楼-- · 2019-04-18 13:44

After working hard and based in the @warakawa answer, I found the solution. As we all know, the implementation of the Listview is causing many headaches, because of this I wrote two classes that fixes the problem of "PREF_SIZE" (which has a constant of 400 for the height and width is calculated according to height). The skin class that I wrote calculates the size as we expect and also prevents ugly horizontal bar when the new property "fillWidth" is changed to "true", this property causes the cells to grow horizontally as much as possible. Regards.

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>
}
查看更多
登录 后发表回答