Подтвердить что ты не робот

Как вы получаете JavaFX ListView как высоту своих элементов?

Если я создаю ListView:

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

Я ожидаю, что он создаст ListView с тремя строками. Но это не так. Он создает ListView из 17 или около того строк. Есть ли способ показать, что ListView всегда будет такой высотой, что все элементы в нем всегда отображаются, но нет пустых строк?

Будет также полезно использовать автоматическую ширину, поэтому она всегда будет такой же широкой, как и самая широкая строка.

Одна из целей заключается в том, что тогда его можно использовать в ScrollPane. Я знаю, что у него есть собственные полосы прокрутки, но они не обеспечивают достаточного контроля.

4b9b3361

Ответ 1

К сожалению, нет хорошего, чистого размера Property of ObservableList, к которому мы привязаны. Вместо этого это можно сделать, добавив List List List List в список, по крайней мере, то, как я это делал в прошлом. По умолчанию размер каждой строки должен быть 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);

Теперь нам нужно добавить ListChangeListener в ObservableList. Когда список изменяется, мы просто меняем высоту набора ListView, чтобы соответствовать новому количеству строк. Если мы знаем, что мы никогда не будем добавлять или удалять элементы из ObservableList, поддерживающие ListView, то мы можем исключить слушателя. Опять же, высота - это количество строк, умноженное на высоту на строку плюс два дополнительных пикселя для границ:

/*
 * 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

Ответ 2

Я только что узнал, что ответ Пола Маршалла может быть сокращен до однострочного с помощью Bindings.size, который создает числовое свойство jfx, представляющее размер ObservableList:

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

Высота ячейки списка, к сожалению, должна быть жестко закодирована AFAIK.

Ответ 3

Вы ищете это:

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

Это скроет пустые ячейки.

Ответ 4

Я нашел относительно легкое, хотя все еще слегка взломанное решение, которое работает в предположении, что все непустые ячейки имеют одинаковую высоту: вместо синтаксического анализа css или некоторого такого, добавьте InvalidationListener к вашему listView.getItems(); в первый раз, когда список ваших элементов становится непустым, вы рекурсивно проходите через дочерние элементы ListView, пока не найдете экземпляр ListCell, где !cell.isEmpty(), и сохраните значение cell.getHeight(). Не забудьте обернуть этот код в Platform.runLater(), чтобы он выполнялся после макетирования ListView. Когда у вас есть эта высота, вы умножаете ее на listView.getItems().size() и вызываете listView.setMaxHeight с результирующим значением (все еще внутри InvalidationListener).

Ответ 5

Система репутации StackOverflow мешает мне комментировать ответ Пола Маршалла, но я хотел добавить, чтобы кто-то еще смотрел на это, что его оценка 24px для строк "вообще" подтверждается официальной документацией JavaFX - см. "fixedCellSize" в http://download.java.net/jdk8/jfxdocs/javafx/scene/control/ListView.html:

Обычно ячейки составляют около 24px...

Таким образом, хотя я согласен с тем, что "[f] изгнание высоты строки, ширины границы и т.д., как-то из стиля или сложного материала беспорядочно, но, вероятно, единственный способ сделать это" может быть правдой, начиная с предположения, что подкрепляется официальной документацией, это хорошее место для начала и, похоже, приводит к приличным спискам в моем тестировании в ListView (с использованием Java 7).

Ответ 6

Поработав и основываясь на ответе @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 есть некоторые исправления. Где я передаю методы по ссылке напрямую, они должны быть сохранены ранее в переменной, а затем вы передаете переменную в методах add/attachments/remove/detachments. В противном случае это может вызвать проблемы с памятью.

Ответ 7

Попробуйте setPrefHeight (double) в подклассе ListCell. Например, в моем коде

@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));
  }
}

Ответ 9

Я основал свой ответ на ответах, предоставленных @scribebleslims и @Alexandre Mazari

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

Вы должны определить LIST_CELL_HEIGHT по своему усмотрению.

В моем случае я использовал 29,501.