@warakawaの回答に基づいて一生懸命働いた後、解決策を見つけました。ご存知のように、Listview の実装は多くの頭痛の種を引き起こしています。このため、「PREF_SIZE」の問題を修正する 2 つのクラスを作成しました (高さの定数は 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 クラスで行うべきいくつかの修正があります。メソッドを直接参照渡しする場合は、前に変数に格納してから、追加/添付/削除/分離メソッドで変数を渡します。そうしないと、メモリの問題が発生する可能性があります。