7

I'm trying to get some information about the ScrollBar components that are by standard included in a ScrollPane. Especially i'm interested in reading the height of the horizontal Scrollbar. How can i reference it?

Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
dajood
  • 3,758
  • 9
  • 46
  • 68

4 Answers4

8

I think you can use the lookupAll() method of the Node class for find the scroll bars. http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#lookupAll(java.lang.String)

For example:

package com.test;

import java.util.Set;
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPaneBuilder;
import javafx.scene.text.Text;
import javafx.scene.text.TextBuilder;
import javafx.stage.Stage;

public class JavaFxScrollPaneTest extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        String longString = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.";
        Text longText = TextBuilder.create().text(longString).build();

        ScrollPane scrollPane = ScrollPaneBuilder.create().content(longText).build();
        primaryStage.setScene(new Scene(scrollPane, 400, 100));
        primaryStage.show();

        Set<Node> nodes = scrollPane.lookupAll(".scroll-bar");
        for (final Node node : nodes) {
            if (node instanceof ScrollBar) {
                ScrollBar sb = (ScrollBar) node;
                if (sb.getOrientation() == Orientation.HORIZONTAL) {
                    System.out.println("horizontal scrollbar visible = " + sb.isVisible());
                    System.out.println("width = " + sb.getWidth());
                    System.out.println("height = " + sb.getHeight());
                }
            }
        }
    }
}
ytw
  • 1,335
  • 2
  • 20
  • 42
  • Works for me (JavaFX 8) in run-time – [http://stackoverflow.com/questions/28904312/how-to-access-scrollbar-in-a-scrollpane-javafx](http://stackoverflow.com/questions/28904312/how-to-access-scrollbar-in-a-scrollpane-javafx). – David Jan 03 '17 at 15:38
6

Since the mentioned methods did not work for everybody (including me), I investigated it a bit more and found the source of the problem.

In general, both methods work, but only as soon as the ScrollPane's skin property has been set. In my case, skin was still null after loading my view using FXMLLoader.

By delaying the call in case the skin property has not been initialized (using a one-shot listener) solves the problem.

Working boiler-plate code:

ScrollPane scrollPane;
// ...
if (scrollPane.getSkin() == null) {
    // Skin is not yet attached, wait until skin is attached to access the scroll bars
    ChangeListener<Skin<?>> skinChangeListener = new ChangeListener<Skin<?>>() {
        @Override
        public void changed(ObservableValue<? extends Skin<?>> observable, Skin<?> oldValue, Skin<?> newValue) {
            scrollPane.skinProperty().removeListener(this);
            accessScrollBar(scrollPane);
        }
    };
    scrollPane.skinProperty().addListener(skinChangeListener);
} else {
    // Skin is already attached, just access the scroll bars
    accessScrollBar(scrollPane);
}

private void accessScrollBar(ScrollPane scrollPane) {
    for (Node node : scrollPane.lookupAll(".scroll-bar")) {
        if (node instanceof ScrollBar) {
            ScrollBar scrollBar = (ScrollBar) node;
            if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
                // Do something with the horizontal scroll bar

                // Example 1: Print scrollbar height
                // System.out.println(scrollBar.heightProperty().get());

                // Example 2: Listen to visibility changes
                // scrollBar.visibleProperty().addListener((observable, oldValue, newValue) -> {
                //     if(newValue) {
                //         // Do something when scrollbar gets visible
                //     } else {
                //         // Do something when scrollbar gets hidden
                //     }
                // });
            }
            if (scrollBar.getOrientation() == Orientation.VERTICAL) {
                // Do something with the vertical scroll bar
            }

        }
    }
}
Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
  • runlater is unsafe: there's no guarantee _when_ it is called, just _sometime later_ which may or may not after the skin is attached. Listening to the scrollPane's skin property and do the lookup after the first change is safe. – kleopatra Apr 30 '19 at 11:28
  • 1
    @kleopatra Thanks for the input! I updated the code to not use `Platform.runLater()` but to listen to a change to the `skin` property. – Markus Weninger Apr 30 '19 at 11:48
5

This not is the best pratice, but works,

private boolean determineVerticalSBVisible(final ScrollPane scrollPane) {
    try {
        final ScrollPaneSkin skin = (ScrollPaneSkin) scrollPane.getSkin();
        final Field field = skin.getClass().getDeclaredField("vsb");
        field.setAccessible(true);
        final ScrollBar scrollBar = (ScrollBar) field.get(skin);
        field.setAccessible(false);
        return scrollBar.isVisible();
    } catch (final Exception e) {
        e.printStackTrace();
    }
    return false;
}

Use "hsb" for Horizontal ScrollBar.

Best Regards, Henrique Guedes.

Guedolino
  • 566
  • 6
  • 2
1

This also works, and I find it easier!

    scrollPane.skinProperty().addListener((skinObservable, oldSkin, newSkin) -> {

       if(newSkin != null) {

           ScrollBar hScrollBar = ((ScrollPaneSkin) newSkin).getHorizontalScrollBar();

           ScrollBar vScrollBar = ((ScrollPaneSkin) newSkin).getVerticalScrollBar();

           // Add event listeners to ScrollBars
           hScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
               System.out.println("Horizontal Scroll Value: " + newValue);
           });

           vScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
               System.out.println("Vertical Scroll Value: " + newValue);
           });

      }

  });
Edward
  • 31
  • 2