I have a horizontal VirtualFlow
and I need to remove vertical ScrollBar
. This is my code:
public class NewMain extends Application {
public class VirtualCell extends IndexedCell<String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(item);
setMinHeight(250); // 250 > scene height
setPrefHeight(250); // 250 > scene height
}
}
@Override
protected Skin<?> createDefaultSkin() {
return new CellSkinBase<>(this);
}
}
private static class MyVirtualFlow extends VirtualFlow<VirtualCell> {
public MyVirtualFlow() {
getVbar().setVisible(false);
setVertical(false);
}
}
private MyVirtualFlow virtualFlow = new MyVirtualFlow();
@Override
public void start(Stage primaryStage) {
List<String> list = new ArrayList<>();
for (var i = 0; i < 100; i++) {
list.add("Item " + i);
}
ObservableList<String> items = FXCollections.observableArrayList(list);
virtualFlow.setCellFactory(vf -> new VirtualCell());
virtualFlow.setCellCount(items.size());
virtualFlow.setCellFactory(vf -> new VirtualCell() {
@Override
public void updateIndex(int index) {
super.updateIndex(index);
if (index >= 0 && index < items.size()) {
updateItem(items.get(index), false);
} else {
updateItem(null, true);
}
}
});
VBox.setVgrow(virtualFlow, Priority.ALWAYS);
var root = new VBox(this.virtualFlow);
Scene scene = new Scene(root, 400, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
As you see I tried to remove it using getVbar().setVisible(false);
in VirtualFlow
constructor. But it didn't work. Could anyone say how to do it , if it is possible.
I have a horizontal VirtualFlow
and I need to remove vertical ScrollBar
. This is my code:
public class NewMain extends Application {
public class VirtualCell extends IndexedCell<String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(item);
setMinHeight(250); // 250 > scene height
setPrefHeight(250); // 250 > scene height
}
}
@Override
protected Skin<?> createDefaultSkin() {
return new CellSkinBase<>(this);
}
}
private static class MyVirtualFlow extends VirtualFlow<VirtualCell> {
public MyVirtualFlow() {
getVbar().setVisible(false);
setVertical(false);
}
}
private MyVirtualFlow virtualFlow = new MyVirtualFlow();
@Override
public void start(Stage primaryStage) {
List<String> list = new ArrayList<>();
for (var i = 0; i < 100; i++) {
list.add("Item " + i);
}
ObservableList<String> items = FXCollections.observableArrayList(list);
virtualFlow.setCellFactory(vf -> new VirtualCell());
virtualFlow.setCellCount(items.size());
virtualFlow.setCellFactory(vf -> new VirtualCell() {
@Override
public void updateIndex(int index) {
super.updateIndex(index);
if (index >= 0 && index < items.size()) {
updateItem(items.get(index), false);
} else {
updateItem(null, true);
}
}
});
VBox.setVgrow(virtualFlow, Priority.ALWAYS);
var root = new VBox(this.virtualFlow);
Scene scene = new Scene(root, 400, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
As you see I tried to remove it using getVbar().setVisible(false);
in VirtualFlow
constructor. But it didn't work. Could anyone say how to do it , if it is possible.
1 Answer
Reset to default 4Firstly I agree with all the suggestions provided by @jewelsea and @slaw. You are defying the purpose of VirtualFlow. But if you desperately want to give a try, below is a way to can try to approach.
Note: I am not recommending this approach. The sole purpose is to let you know why setting visible will not fix and a workaround for that.
Though the scroll bar is initiated only once per VirtualFlow instance, its visibility is computed dynamically based on the current state of the VirtualFlow. The scrollbars visibility is recomputed in layoutChildren()
method by calling computeBarVisiblity()
. The code of this method is as below.
private boolean computeBarVisiblity() {
if (cells.isEmpty()) {
// In case no cells are set yet, we assume no bars are needed
needLengthBar = false;
needBreadthBar = false;
return true;
}
final boolean isVertical = isVertical();
boolean barVisibilityChanged = false;
VirtualScrollBar breadthBar = isVertical ? hbar : vbar;
VirtualScrollBar lengthBar = isVertical ? vbar : hbar;
final double viewportBreadth = getViewportBreadth();
final int cellsSize = cells.size();
final int cellCount = getCellCount();
for (int i = 0; i < 2; i++) {
final boolean lengthBarVisible = getPosition() > 0
|| cellCount > cellsSize
|| (cellCount == cellsSize && (getCellPosition(cells.getLast()) + getCellLength(cells.getLast())) > getViewportLength())
|| (cellCount == cellsSize - 1 && barVisibilityChanged && needBreadthBar);
if (lengthBarVisible ^ needLengthBar) {
needLengthBar = lengthBarVisible;
barVisibilityChanged = true;
}
final boolean breadthBarVisible = !suppressBreadthBar && (maxPrefBreadth > viewportBreadth);
if (breadthBarVisible ^ needBreadthBar) {
needBreadthBar = breadthBarVisible;
barVisibilityChanged = true;
}
}
// Start by optimistically deciding whether the length bar and
// breadth bar are needed and adjust the viewport dimensions
// accordingly. If during layout we find that one or the other of the
// bars actually is needed, then we will perform a cleanup pass
if (!Properties.IS_TOUCH_SUPPORTED) {
updateViewportDimensions();
breadthBar.setVisible(needBreadthBar);
lengthBar.setVisible(needLengthBar);
} else {
breadthBar.setVisible(needBreadthBar && tempVisibility);
lengthBar.setVisible(needLengthBar && tempVisibility);
}
return barVisibilityChanged;
}
So setting the visibility at only one point will not work and will be overriden.
One way to fix this, is by setting the prefWidth of the vBar to 0px, so that the layout computation will ignore the Vbar. But setting prefWidth to 0px alone is not sufficient. The arrows of inc/dec button are still visible (I have not yet checked why it is happening..). To fix that, you can set the opacity of VBar to 0.
Once you update the below changes to MyVirtualFlow, the VBar is not visible anymore.
Option#1: (using API)
private static class MyVirtualFlow extends VirtualFlow<VirtualCell> {
public MyVirtualFlow() {
setVertical(false);
getVbar().setPrefWidth(0);
getVbar().setOpacity(0);
}
}
Option#2: (using CSS)
private static class MyVirtualFlow extends VirtualFlow<VirtualCell> {
private final static String CSS = "data:text/css," +
"""
.virtual-flow > .scroll-bar:vertical{
-fx-pref-width: 0px;
}
.virtual-flow > .scroll-bar:vertical .increment-arrow,
.virtual-flow > .scroll-bar:vertical .decrement-arrow{
-fx-padding: 0px;
}
""";
public MyVirtualFlow() {
setVertical(false);
getStylesheets().add(CSS);
}
}
VirtualFlow
are one of its design features. If you do not want this feature, you should not useVirtualFlow
rather than trying to make it not do something it was designed to do. Just my opinion. – jewelsea Commented Mar 17 at 21:01VirtualFlow
is not the same as Flowless. A policy for the control of the scroll function is not a feature ofVirtualFlow
. Your opinion on whether it should be a feature doesn't matter, as that is not the way the control was designed. The ability to not display a node in the scene by making the node invisible is standard for JavaFX (though the invisible node will still take up space unless you make it unmanaged and other layout code stops accounting for it). – jewelsea Commented Mar 17 at 22:04VirtualFlow
design. – jewelsea Commented Mar 17 at 22:07VirtualFlow
. But unfortunately, what you want is not currently supported. One workaround that might work is setting the scroll bars' min/pref/max width/height to 0 via CSS. – Slaw Commented Mar 17 at 22:51VirtualFlow
manages its own scroll bars, and does so in computing its layout each time it performs a layout pass. If you make the content taller than the space it has available, it will add a vertical scroll bar. Use the API the way it’s designed. – James_D Commented Mar 18 at 2:18