Javafx 2 如何使JavaFXListView成为其项目的高度?
如果创建列表视图:Javafx 2 如何使JavaFXListView成为其项目的高度?,javafx-2,Javafx 2,如果创建列表视图: new ListView<>(FXCollections.observableArrayList("1", "2", "3")) /* * 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; fi
new ListView<>(FXCollections.observableArrayList("1", "2", "3"))
/*
* 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);
newlistview(FXCollections.observearraylist(“1”、“2”、“3”))
我希望它创建一个包含3行的ListView。但事实并非如此。它创建了一个大约17行的列表视图。是否有一种方法可以告诉ListView始终为高度,以便始终显示其中的任何项目,但不显示空白行
让它自动宽度也很有用,所以它总是和最宽的行一样宽
这样做的一个目的是可以在滚动窗格中使用它。我知道它有自己的滚动条,但它们不能提供足够的控制。不幸的是,ObservableList没有一个好的、干净的大小属性供我们绑定。相反,在列表中添加一个ListChangeListener是可行的,至少我过去是这样做的。默认情况下,每行的大小应该是24px,并且我们需要在列表视图的顶部和底部添加一个额外的像素。否则,我们仍然会显示ListView的滚动条。首先,我们将创建ObservableList和ListView,并设置ListView的初始高度:
new ListView<>(FXCollections.observableArrayList("1", "2", "3"))
/*
* 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的设置高度以匹配新的行数。如果我们知道永远不会从支持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);
}
});
参考资料:
,StackOverflow的声誉系统阻止我对Paul Marshall的答案发表评论,但我想补充一点,让其他人看看,他对行的24px估计值“一般”得到了官方JavaFX文档的确认-请参阅以下“fixedCellSize”: 一般情况下,电池的容量约为24px
因此,虽然我同意“从样式或复杂的东西中找出行高、边框宽度等可能会很混乱,但可能是唯一的方法”可能是正确的,但从一个有官方文档支持的假设开始是一个很好的开始,在我的ListView测试中,似乎会得到外观不错的列表(使用Java 7)。您正在搜索:
.list-cell:empty {
-fx-opacity: 0;
}
这将隐藏空单元格。我刚刚发现Paul Marshall的答案可以简化为一行,使用它创建一个表示可观察列表大小的数值jfx属性:
listView.prefHeightProperty().bind(Bindings.size(itemListProperty).multiply(LIST_CELL_HEIGHT));
很遗憾,列表单元格的高度仍然必须是硬编码的。尝试在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));
}
}
我发现了一个相对简单但仍然有点粗糙的解决方案,它在假设所有非空单元格都具有相同高度的情况下工作:与其解析css或类似的内容,不如在
listView.getItems()中添加一个InvalizationListener
;当您的项目列表第一次变为非空时,您会递归地遍历列表视图的子项,直到找到列表单元格的一个实例,其中!cell.isEmpty()
,并存储单元格.getHeight()
。请确保将该代码包装在平台中。runLater()
以便在完成ListView
布局后执行它。获得该高度后,将其与ListView.getItems().size()
相乘,并使用结果值调用ListView.setMaxHeight
(仍在InvalizationListener
中).经过努力,基于@warakawa的答案,我找到了解决方案。众所周知,Listview的实现带来了很多麻烦,因此我编写了两个类,解决了“PREF_SIZE”(高度和宽度的常量为400,根据高度计算)的问题。我编写的skin类按照我们的预期计算大小,并且在新属性“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>
}
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>
}
封装javafxapplication;
导入javafx.beans.property.BooleanProperty;
导入javafx.beans.property.SimpleBoleAnProperty;
导入javafx.scene.control.Skin;
公共类ListViewFixed扩展了javafx.scene.control.ListView
{
//
private final BooleanProperty fillWidth=新的SimpleBoleanProperty(即“fillWidth”);
公共最终布尔属性fillWidthProperty()
{
返回填充宽度;
}
公共最终布尔值isFillWidth()
{
返回fillWidth.get();
}
公共最终空心集fillWidth(布尔fillWidth)
{
this.fillWidth.set(fillWidth);
}
//
//
@凌驾
受保护的皮肤createDefaultSkin()
{
返回新的ListViewFixedSkin(此);
}
//
}
ListViewFixedSkin.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>
}
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>
}
封装javafxapplication;
导入java.util.Set;
导入javafx.beans.Observable;
导入javafx.geometry.Insets;
导入javafx.geometry.Orientation;
导入javafx.scene.Node;
导入javafx.scene.control.IndexedCell;
导入javafx.scene.control.ScrollBar;
导入javafx.scene.layout.Region;
公共类ListViewFixedSkin扩展了com.sun.javafx.scene.control.skin.ListViewSkin
{
//
私有listView固定listView;
私有滚动条滚动条水平;
私有滚动条滚动条垂直;
私有布尔缓存;
专用双缓存;
私有区域;
//
//
公共ListViewFixedSkin(ListViewFixedListView)
{
超级(列表视图);
this.listView=listView;
registerChangeListener(listView.fillWidthProperty(),“填充宽度”);
}
//
//
私有void updateFillWidth()
{
if(scrollBarHorizontal!=null&&scrollBarVertical!=null&&fillWidthCache!=listView.isFillWidth())
{
if(listView.isFillWidth()