6

I need to know how to change the amount that a ScrollPane object use to move the content up and down. For example, a ScrollBar have these two methods for that:

scrollBar.setUnitIncrement(10);     
scrollBar.setBlockIncrement(50);  

How do I do the same with a ScrollPane instead of ScrollBar?

Hasen
  • 11,710
  • 23
  • 77
  • 135
usertest
  • 2,140
  • 4
  • 32
  • 51
  • same as [older question](https://stackoverflow.com/q/17687188/203657) - not marking as duplicate because the answers are a bit different, also note the [very old rfe - 2011!](https://bugs.openjdk.java.net/browse/JDK-8091864) referenced over there – kleopatra Dec 17 '19 at 11:19

7 Answers7

6

Faster vertical scroll in a ScrollPane:

    final double SPEED = 0.01;
    scrollPane.getContent().setOnScroll(scrollEvent -> {
        double deltaY = scrollEvent.getDeltaY() * SPEED;
        scrollPane.setVvalue(scrollPane.getVvalue() - deltaY);
    });
Jack J
  • 1,514
  • 3
  • 20
  • 28
5

In an external style sheet you can do

.scroll-pane .scroll-bar:vertical {
    -fx-unit-increment: 10 ;
    -fx-block-increment: 50 ;
}

.scroll-pane .scroll-bar:horizontal {
    -fx-unit-increment: 5 ;
    -fx-block-increment: 20 ;
}

for example. If you want it to apply to just a single scroll pane, give the scroll pane an id, etc.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • 7
    Thanks a lot man, this did change the amount by which the scroll bar is adjusted when a button {up, down arrows} is clicked. but not the mouse wheel, is there a solution for that?? – usertest Sep 26 '15 at 10:10
  • 1
    Works fine with JDK 11. Thanks James! – KenobiBastila Nov 11 '19 at 14:47
  • I'm a bit confused as to why this would work though. Maybe it worked for a older JFX version? Because as far as I know internally (ScrollPaneSkin) the scroll pane computes the scroll bar's unit increment relative to the content so that no matter the content size the scroll is always consistent – Alessandro Parisi Jun 23 '22 at 09:49
5

Had the same question and was unable to find the answer. Finally got the correct approach from the link provided by Erik and adapted it to my needs.

As the scrollPane itself did not fire the "setOnScroll"-Event (possibly because it gets consumed before doing so), I had to set the EventHandler on the content of the ScrollPane:

scrollPane_Content.setContent(vBox_Content);

    vBox_Content.setOnScroll(new EventHandler<ScrollEvent>() {
        @Override
        public void handle(ScrollEvent event) {
            double deltaY = event.getDeltaY()*6; // *6 to make the scrolling a bit faster
            double width = scrollPane_Content.getContent().getBoundsInLocal().getWidth();
            double vvalue = scrollPane_Content.getVvalue();
            scrollPane_Content.setVvalue(vvalue + -deltaY/width); // deltaY/width to make the scrolling equally fast regardless of the actual width of the component
        }
    });

Doing so works with using the scrollbar directly as well as using the mousewheel to scroll up and down.

Hope this helps others who are looking for speeding up scrolling on a scrollPane.

Community
  • 1
  • 1
Andreas Koch
  • 59
  • 1
  • 2
  • 1
    The example you're quoting is set up to scroll horizontally instead of vertically. I think that the things you do with the width may not be needed? – Doctor Parameter Dec 22 '17 at 14:33
  • @DoctorParameter yes, you need this the width param, as this solution already is for vertical scrolling, not horizontal scrolling – Brainiac Sep 10 '19 at 08:42
  • 1
    The this changes the vertical scroll according to the content _width_... See my answer for solution using the difference between content and scroll pane _height_. – geometrikal May 08 '21 at 02:36
3

This fixes the scrolling by looking at the difference in height between the content and the scroll pane so that the amount scrolled is the same.

//Fix scroll pane slow scrolling
scrollPane.getContent().setOnScroll(scrollEvent -> {
    double deltaY = scrollEvent.getDeltaY();
    double contentHeight = scrollPane.getContent().getBoundsInLocal().getHeight();
    double scrollPaneHeight = scrollPane.getHeight();
    double diff = contentHeight - scrollPaneHeight;
    if (diff < 1) diff = 1;
    double vvalue = scrollPane.getVvalue();
    scrollPane.setVvalue(vvalue + -deltaY/diff);
});

The difference is set to a minimum value of 1 to prevent divide by zero.

geometrikal
  • 3,195
  • 2
  • 29
  • 40
0

What I wanted was simple setting, that would work everywhere. Since no good answer was given I did a bit of research on my own. Maybe the solution will help you too.

Since I wanted smooth scrolling, I created transition class:

import javafx.animation.Transition;
import javafx.util.Duration;

public abstract class SmoothishTransition extends Transition {
    private final double mod;
    private final double delta;

    private final static int TRANSITION_DURATION = 200;

    public SmoothishTransition(SmoothishTransition old, double delta) {
        setCycleDuration(Duration.millis(TRANSITION_DURATION));
        setCycleCount(0);
        // if the last transition was moving inthe same direction, and is still playing
        // then increment the modifer. This will boost the distance, thus looking faster
        // and seemingly consecutive.
        if (old != null && sameSign(delta, old.delta) && playing(old)) {
            mod = old.getMod() + 1;
        } else {
            mod = 1;
        }
        this.delta = delta;
    }

    public double getMod() {
        return mod;
    }

    @Override
    public void play() {
        super.play();
        // Even with a linear interpolation, startup is visibly slower than the middle.
        // So skip a small bit of the animation to keep up with the speed of prior
        // animation. The value of 10 works and isn't noticeable unless you really pay
        // close attention. This works best on linear but also is decent for others.
        if (getMod() > 1) {
            jumpTo(getCycleDuration().divide(10));
        }
    }

    private static boolean playing(Transition t) {
        return t.getStatus() == Status.RUNNING;
    }

    private static boolean sameSign(double d1, double d2) {
        return (d1 > 0 && d2 > 0) || (d1 < 0 && d2 < 0);
    }
}

Then this class takes care of scrolling:

import javafx.animation.Interpolator;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.ScrollEvent;

public class SmoothScroll {

    private final static double BASE_MODIFIER = 1;

    public SmoothScroll(final ScrollPane scrollPane, final Node node) {
        this(scrollPane, node, 160);
    }
    public SmoothScroll(final ScrollPane scrollPane, final Node node, final double baseChange) {
        node.setOnScroll(new EventHandler<ScrollEvent>() {
            private SmoothishTransition transition;

            @Override
            public void handle(ScrollEvent event) {
                if (scrollPane==null) {
                    return;
                }
                double deltaYOrg = event.getDeltaY();
                if (deltaYOrg==0) {
                    return;
                }
                double percents = calculatePercents(deltaYOrg>=0);

                final double startingVValue = scrollPane.getVvalue();

                smoothTransition(
                        startingVValue,
                        getFinalVValue(startingVValue, percents),
                        BASE_MODIFIER * deltaYOrg
                );
            }

            private void smoothTransition(double startingVValue, double finalVValue, double deltaY) {
                Interpolator interp = Interpolator.LINEAR;
                transition = new SmoothishTransition(transition, deltaY) {
                    @Override
                    protected void interpolate(double frac) {
                        scrollPane.setVvalue(
                                interp.interpolate(startingVValue, finalVValue, frac)
                        );
                    }
                };
                transition.play();
            }

            private double getFinalVValue(double startingVValue, double percents) {
                double finalVValueToSet = startingVValue + percents;
                if (finalVValueToSet>1) {
                    return 1d;
                }
                if (finalVValueToSet<0) {
                    return 0d;
                }
                return finalVValueToSet;
            }

            private double calculatePercents(boolean positive) {
                double fullHeight = scrollPane.getContent().getBoundsInLocal().getHeight();
                double viewableHeight = scrollPane.getBoundsInLocal().getHeight();
                double fullChangeInHeight = fullHeight-viewableHeight;
                double percents = baseChange /fullChangeInHeight;
                if (positive) {
                    percents = percents*-1;
                }
                return percents;
            }
        });
    }
}

And the usage:

new SmoothScroll(mainScroll, mainVbox);

or

new SmoothScroll(mainScroll, mainVbox, 50);

This code should get you started.

Matjaz
  • 468
  • 5
  • 21
0

None of the solutions posted here worked for me. I'm using Java 11.0.2, that might be the issue. The way I fixed it is with this code:

final double boost = 0.025;
scrollPane.vvalueProperty().addListener(
    (ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
        //System.out.println("from "+oldValue+" to "+newValue);
        double diff = (double) newValue - (double) oldValue;
        if (almostEqual(diff, boost, 0.00001) || almostEqual(diff, (0 - boost), 0.00001) || almostEqual(diff, 0, 0.000001)) {
            return;
        }
        double newww = scrollPane.getVvalue() + (diff > 0 ? boost : (0 - boost));
        //System.out.println("\tnewww val - "+newww+"; diff - "+diff);
        if (0 < newww && newww < 1) {
            //System.out.println("\tsetting value");
            scrollPane.setVvalue(newww);
        }
    });

Using additional method for comparison of doubles:

public static boolean almostEqual(double a, double b, double eps) {
    return Math.abs(a - b) < eps;
}
orzen
  • 1
  • 1
-3

You can use this:

scrollPane.getVerticalScrollBar().setUnitIncrement(20);
Erik Pragt
  • 13,513
  • 11
  • 58
  • 64
  • Thanks for reply, that method does not exists, are you talking about JavaFX ScrollPane, because that is the class, and there is no such method. am i missing some thing here ??? – usertest Sep 23 '15 at 12:25
  • Ah, my bad. I missed the javafx tag, I thought it was Swing! – Erik Pragt Sep 23 '15 at 13:35
  • Did you see this comment: http://stackoverflow.com/questions/17687188/how-can-i-set-the-unit-increment-for-a-scroll-pane-in-javafx ? – Erik Pragt Sep 23 '15 at 13:36
  • so it's not an answer, is it ;) You might consider deleting it .. – kleopatra Dec 17 '19 at 11:11