Dej*_*jwi 5 java listview javafx
我试图避免在ListView中水平滚动。ListView实例保存HBox项的列表,每个项具有不同的宽度。
到目前为止,我正在使用这样的单元工厂:
public class ListViewCell extends ListCell<Data>
{
@Override
public void updateItem(Data data, boolean empty)
{
super.updateItem(data, empty);
if(empty || data == null){
setGraphic(null);
setText(null);
}
if(data != null)
{
Region region = createRow(data);
region.prefWidthProperty().bind(mListView.widthProperty().subtract(20));
region.maxWidthProperty().bind(mListView.widthProperty().subtract(20));
setGraphic(region);
}
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这还不够。通常在添加几个项目之后,ListView的水平滚动条就会出现。即使似乎没有必要。
如何确保ListViewCell不会超过其父级宽度,并且水平滚动条不会出现?
这里有很多因素使得自定义 ListView 水平滚动条行为变得难以处理。除此之外,对 ListView 工作原理的常见误解可能会导致其他问题。
要解决的主要问题是当垂直滚动条可见时,ListCells 的宽度不会自动调整。因此,就在此时,内容突然太宽,无法容纳在 ListView 的左边缘和垂直滚动条的左边缘之间,从而触发了水平滚动条。在确定要设置的正确绑定时,还需要考虑 ListCell 的默认填充以及 ListView 本身的边框宽度。
下面的类扩展了 ListView:
public class WidthBoundList extends ListView {
private final BooleanProperty vbarVisibleProperty = new SimpleBooleanProperty(false);
private final boolean bindPrefWidth;
private final double scrollbarThickness;
private final double sumBorderSides;
public WidthBoundList(double scrollbarThickness, double sumBorderSides, boolean bindPrefWidth) {
this.scrollbarThickness = scrollbarThickness;
this.sumBorderSides = sumBorderSides;
this.bindPrefWidth = bindPrefWidth;
Platform.runLater(()->{
findScroller();
});
}
private void findScroller() {
if (!this.getChildren().isEmpty()) {
VirtualFlow flow = (VirtualFlow)this.getChildren().get(0);
if (flow != null) {
List<Node> flowChildren = flow.getChildrenUnmodifiable();
int len = flowChildren .size();
for (int i = 0; i < len; i++) {
Node n = flowChildren .get(i);
if (n.getClass().equals(VirtualScrollBar.class)) {
final ScrollBar bar = (ScrollBar) n;
if (bar.getOrientation().equals(Orientation.VERTICAL)) {
vbarVisibleProperty.bind(bar.visibleProperty());
bar.setPrefWidth(scrollbarThickness);
bar.setMinWidth(scrollbarThickness);
bar.setMaxWidth(scrollbarThickness);
} else if (bar.getOrientation().equals(Orientation.HORIZONTAL)) {
bar.setPrefHeight(scrollbarThickness);
bar.setMinHeight(scrollbarThickness);
bar.setMaxHeight(scrollbarThickness);
}
}
}
} else {
Platform.runLater(()->{
findScroller();
});
}
} else {
Platform.runLater(()->{
findScroller();
});
}
}
public void bindWidthScrollCondition(Region node) {
node.maxWidthProperty().unbind();
node.prefWidthProperty().unbind();
node.maxWidthProperty().bind(
Bindings.when(vbarVisibleProperty)
.then(this.widthProperty().subtract(scrollbarThickness).subtract(sumBorderSides))
.otherwise(this.widthProperty().subtract(sumBorderSides))
);
if (bindPrefWidth) {
node.prefWidthProperty().bind(node.maxWidthProperty());
}
}
}
Run Code Online (Sandbox Code Playgroud)
关于您的代码,您的绑定可能会导致问题。ListCell 的updateItem()方法不仅在创建 ListCell 时被调用。ListView 可以包含相当大的数据列表,因此为了提高性能,只有滚动到视图中的 ListCell(可能还有之前和之后的一些)需要渲染图形。该updateItem()方法处理这个问题。在您的代码中,会一遍又一遍地创建一个区域,并且每个区域都绑定到 ListView 的宽度。相反,ListCell 本身应该被绑定。
下面的类扩展了ListCell,并在构造函数中调用了绑定HBox的方法:
public class BoundListCell extends ListCell<String> {
private final HBox hbox;
private final Label label;
public BoundListCell(WidthBoundList widthBoundList) {
this.setPadding(Insets.EMPTY);
hbox = new HBox();
label = new Label();
hbox.setPadding(new Insets(2, 4, 2, 4));
hbox.getChildren().add(label);
widthBoundList.bindWidthScrollCondition(this);
}
@Override
public void updateItem(String data, boolean empty) {
super.updateItem(data, empty);
if (empty || data == null) {
label.setText("");
setGraphic(null);
setText(null);
} else {
label.setText(data);
setGraphic(hbox);
}
}
}
Run Code Online (Sandbox Code Playgroud)
WidthBoundList构造函数的参数scrollbarThickness已设置为12。该sumBorderSides参数已设置为2,因为我的WidthBoundList左右各有一个像素边框。该bindPrefWidth参数已设置为 true 以防止水平滚动条完全显示(标签有省略号,您可能添加到水平盒子的任何非文本节点都将被剪掉)。设置bindPrefWidth为 false 以允许水平滚动条,并且通过这些正确的绑定,它应该仅在需要时显示。一个实现:
private final WidthBoundList myListView = new WidthBoundList(12, 2, true);
public static void main(final String... a) {
Application.launch(a);
}
@Override
public void start(final Stage primaryStage) throws Exception {
myListView.setCellFactory(c -> new BoundListCell(myListView));
VBox vBox = new VBox();
vBox.setFillWidth(true);
vBox.setAlignment(Pos.CENTER);
vBox.setSpacing(5);
Button button = new Button("APPEND");
button.setOnAction((e)->{
myListView.getItems().add("THIS IS LIST ITEM NUMBER " + myListView.getItems().size());
});
vBox.getChildren().addAll(myListView, button);
myListView.maxWidthProperty().bind(vBox.widthProperty().subtract(20));
myListView.prefHeightProperty().bind(vBox.heightProperty().subtract(20));
primaryStage.setScene(new Scene(vBox, 200, 400));
primaryStage.show();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1766 次 |
| 最近记录: |