When I use some components which contains scrollbars, such as TextArea, ListView and ScrollPane, when the content is very long, the scrollbar's thumb become very small. How can I set a min len of the thumb?
At this stage there is no direct way to limit the size of the thumb.
But with some little tweaking you can get the desired behavior ;-). If you are OK with using the lookup() method, please check the below approach.
The general idea is to lookup for the "thumb" and dynamically update its dimensions, whenever its height/position is changed.
Below is a utility class for this purpose, to which, you can pass the required control and utility will ensure to search and add the required updates to the vertical scroll bar.
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Orientation;
import javafx.scene.control.Control;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.Skin;
import javafx.scene.layout.StackPane;
import java.util.Optional;
public class ScrollThumbSizing {
private static final double THUMB_MIN_HEIGHT = 50;
private static final String THUMB_STYLECLASS = ".thumb";
private static final String DISABLE_LISTENERS = "listener";
private static final String ACTUAL_HEIGHT = "height";
public static void enable(final Control control) {
final ChangeListener<Skin<?>> changeListener = (obs, old, skin) -> {
if (skin != null) {
searchScrollBar(control);
}
};
control.skinProperty().addListener(changeListener);
if (control.getSkin() != null) {
searchScrollBar(control);
}
}
private static void searchScrollBar(final Control control) {
// When the skin is loaded, let the things settle and do search operation at later stage
Platform.runLater(() -> {
// Find the vertical scroll bar
Optional<ScrollBar> scrollBar = control.lookupAll(".scroll-bar").stream()
.map(node -> (ScrollBar) node)
.filter(sb -> sb.getOrientation() == Orientation.VERTICAL)
.findFirst();
scrollBar.ifPresent(sb -> {
final StackPane thumb = (StackPane) sb.lookup(THUMB_STYLECLASS);
if (thumb != null) {
// When the thumb exists, and if the height/position is changed, recompute the thumb dimensions.
final ChangeListener<? super Number> listener = (obs1, old1, val) -> adjustThumb(sb, thumb);
thumb.heightProperty().addListener(listener);
thumb.translateYProperty().addListener(listener);
adjustThumb(sb, thumb);
}
});
});
}
private static void adjustThumb(final ScrollBar scrollBar, final StackPane thumb) {
final double thumbHeight = thumb.getHeight();
/* Caching the actual thumb height if it is less than the minimum height */
if (thumbHeight < THUMB_MIN_HEIGHT) {
scrollBar.getProperties().put(ACTUAL_HEIGHT, thumbHeight);
} else if (thumbHeight > THUMB_MIN_HEIGHT) {
scrollBar.getProperties().remove(ACTUAL_HEIGHT);
}
/* If the thumb height is less than or equal to the minimum height, adjust the thumb size */
/* DISABLE_LISTENERS key is to NOT let the things happen in loop forever... */
if (scrollBar.getProperties().get(DISABLE_LISTENERS) == null && thumbHeight <= THUMB_MIN_HEIGHT) {
/* Disabling the listeners */
scrollBar.getProperties().put(DISABLE_LISTENERS, true);
/* Resizing the thumb to minimum height */
thumb.resize(scrollBar.getWidth(), THUMB_MIN_HEIGHT);
/* Positioning the thumb when set to minimum height */
final Object thumbActualHgtObj = scrollBar.getProperties().get(ACTUAL_HEIGHT);
final double thumbActualHgt = thumbActualHgtObj == null ? 0 : (double) thumbActualHgtObj;
final double ty = thumb.getTranslateY();
final double sbHeight = scrollBar.getHeight();
final double tyMax = sbHeight - thumbActualHgt;
final double tH = sbHeight - THUMB_MIN_HEIGHT;
thumb.setTranslateY(ty / tyMax * tH);
/* Enabling the listeners */
scrollBar.getProperties().remove(DISABLE_LISTENERS);
}
}
}
For the demonstration purpose, I applied this utility on TextArea, ScrollPane, ListView & TableView. Please check the below demo.
Before setting ScrollThumbSizing:
After setting ScrollThumbSizing (50px):
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ScrollThumbSizing_Demo extends Application {
@Override
public void start(final Stage stage) throws Exception {
GridPane root = new GridPane();
root.setPadding(new Insets(10));
root.setVgap(10);
root.setHgap(10);
ColumnConstraints columnConstraints = new ColumnConstraints();
columnConstraints.setPercentWidth(100);
root.getColumnConstraints().addAll(columnConstraints,columnConstraints);
// TextArea
TextArea textArea = new TextArea();
textArea.setMinHeight(150);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 2000; i++) {
builder.append("abc" + i).append(System.lineSeparator());
}
textArea.setText(builder.toString());
// ListView
ListView<String> listView = new ListView<>();
for (int i = 0; i < 1000; i++) {
listView.getItems().add("Item " + i);
}
// TableView
TableView<String> tableView = new TableView<>();
TableColumn<String, String> col = new TableColumn<>("Column");
col.setCellValueFactory(p -> new SimpleStringProperty(p.getValue()));
tableView.getColumns().add(col);
for (int i = 0; i < 1000; i++) {
tableView.getItems().add("T Item " + i);
}
// ScrollPane
ScrollPane scrollPane = new ScrollPane();
scrollPane.setMinHeight(150);
StackPane content = new StackPane();
content.setMinSize(100, 1500);
content.setStyle("-fx-background-color:linear-gradient(to bottom, gray,red,yellow,green,pink,orange, white);");
scrollPane.setContent(content);
root.addRow(0, textArea, scrollPane);
root.addRow(1, listView, tableView);
Scene scene = new Scene(root, 400,350);
stage.setScene(scene);
stage.setTitle("ScrollThumbSizing Demo");
stage.show();
// You can add this before/after showing the stage.
ScrollThumbSizing.enable(textArea);
ScrollThumbSizing.enable(listView);
ScrollThumbSizing.enable(tableView);
ScrollThumbSizing.enable(scrollPane);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With