2013-07-02 19 views
18

如果我创建一个ListView:你怎么JavaFX的ListView的是其项目的高度?

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

我希望它来创建一个ListView 3行。但事实并非如此。它创建了17行左右的ListView。有没有办法告诉ListView始终是这样的高度:无论哪个项目总是显示,但没有空行?

拥有它自动宽度也将是有益的,所以它总是一样宽,最宽行。这

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

+0

您可以使用[CSS,以便在ListView额外的空行不可见(HTTP:// fxexperience .COM/2011/11 /备用行突出显示功能于空的tableview和 - 列表视图-行/)。这与ListView本身的大小并不相同,但它可能足以满足您的需求。 – jewelsea

+0

@jewelsea我看到了这样的例子,但那不是我所需要的。 – mentics

+0

对于你的要求,我建议在[滚动窗格(HTTP使用[垂直框(http://docs.oracle.com/javafx/2/api/javafx/scene/layout/VBox.html)://文档.oracle.com/javafx/2/api/javafx/scene/control/ScrollPane.html)而不是'ListView'。 – jewelsea

回答

6

不幸的是,没有一个ObservableList我们绑定到一个漂亮的,干净的大小属性。相反,它是通过将列表中的ListChangeListener,至少,这是我如何在过去做了它是可行的。默认情况下,每行的大小应该是24像素,而我们需要的顶部和底部为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的设置高度以匹配新的行数。如果我们知道我们永远不会添加或删除支持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 DocumentationJavaFX ObservableList Documentation

+0

将行高度硬编码为24是不可接受的,并且关于边界的假设也是不可靠的。找出行高度,边框宽度等,不知何故,从造型或棘手的东西是凌乱的,但可能是唯一的方法来做到这一点。谢谢! – mentics

+0

为什么不能'list.lookup(“.list-cell”)。getHeight()'?这允许你在CSS中设置大小。 – Mordechai

1

StackOverflow上的声誉系统阻止我评论保罗·马歇尔答案,但我想添加为别人看着这一点,他的行24px的估计是“一般”,由官方的JavaFX文件证实 - 看“fixedCellSize”在http://download.java.net/jdk8/jfxdocs/javafx/scene/control/ListView.html

一般细胞周围24px的...

因此,虽然我同意“[F] iguring出来行高度,边框宽度等,不知何故从造型还是棘手的东西是乱,但可能是唯一的出路做到这一点“可能是事实,首先是由官方文档备份的假设是一个很好的开始,并且似乎在我对ListView(使用Java 7)进行测试的过程中看起来像样的列表。

+2

这个尺寸可以直接设置,通过样式表修改,基于外观和感觉的不同,平台(android?)等等,所以不管是否存在默认值,它都不是值得依赖的东西。 – mentics

+0

对于任何具有可以修改的默认值的事情都是如此,并且不会删除首先知道默认设置的效用。 –

3

您是否在寻找的是:

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

这将隐藏空单元格的。

5

我刚刚发现,保罗马歇尔的回答可以被还原后使用Bindings.size创建表示ObservableList大小的数字JFX属性一个班轮:

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

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

+0

什么是itemListProperty? – uesports135

+0

想通了。虽然我不得不使用maxHeightProperty – uesports135

0

尝试在ListCell的子类中设置setPrefHeight(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)); 
    } 
} 
3

我发现了一个比较容易但仍略哈克解决方案,它的假设下工作,所有非空细胞具有相同的高度:不是解析CSS或一些这样的,添加InvalidationListener到你的listView.getItems();第一次你的项目列表变为非空,你递归经过ListView的孩子,直到你找到的ListCell其中!cell.isEmpty()一个实例,并存储cell.getHeight()值。要确保这样,一旦ListView布点完成它被执行来包装代码在Platform.runLater()。一旦你的身高,你listView.getItems().size()乘以并调用listView.setMaxHeight与所得到的值(InvalidationListener还在里面)。

+0

酷!喜欢这个。 – Mordechai

+0

实际上不要使用递归,只是'lookup(“.list-cell”)'你就完成了。 – Mordechai

0

在@warakawa答案辛勤工作,基于后,我找到了解决办法。大家都知道,列表视图的实现造成了许多麻烦,因为这个我写的修复“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> 
} 
+0

你如何在你的应用程序中使用它? (尤其是场景构建器) – mrseanpaul81

+0

您将使用其他自定义组件。问候。 –