6

Hi is there any way to increase scroll speed. I found some solutions but none of them works for me. Thats what I tried:

@FXML
private ScrollPane scrollPane;

@FXML
public void initialize() {
    Platform.runLater(() -> setFasterScroller(scrollPane));
}

private static void setFasterScroller(ScrollPane scrollPane) {
    ScrollBar verticalScrollbar = (ScrollBar) scrollPane.lookup(".scroll-bar:vertical");
    double defaultUnitIncrement = verticalScrollbar.getUnitIncrement();
    verticalScrollbar.setUnitIncrement(defaultUnitIncrement * 3);
}

Or alternatively using css:

.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 ;
}

Is there any other way? Edit: None of the solution above worked i tried to use insanely large numbers but the scroll speed was still same

Oleks
  • 1,011
  • 1
  • 14
  • 25
Petr M
  • 143
  • 2
  • 3
  • 12
  • 1
    When you say _none of them works for me_, did you see no effect at all or do you need it still faster? In either case, please [edit] your question to include a [mcve] that exhibits the problem you describe. – trashgod Jun 24 '19 at 16:45
  • I see a distinct change in this [example](https://stackoverflow.com/q/55427306/230513) when altering the `STEP_INCREMENT` passed to `setBlockIncrement()`. – trashgod Jun 26 '19 at 09:12

1 Answers1

1

The app should render scroll pane content 'faster' to increase scrolling speed. You can achieve it reducing the number of objects to be rendered or/and decreasing the number of 'repaint' method invocation (layoutChildren in JFX). As far as I understand the code snippet, you were about to decrease the number of repaints because changing increment params allows repainting the content fewer times.

  • blockIncrement (if the track of the bar is clicked)
  • unitIncrement (if Up, Down, Right, Left buttons are clicked)

When you click scroll pane buttons the method ScrollPane.layoutChildren() is invoked. It makes ScrollBar reset increments (ScrollPaneSkin.updateVerticalSB(), ScrollPaneSkin.updateHorizontalSB()). So you need to store previous increment values and filter 'out of the box' scrolling events.

Source:

public class UnitIncrementScrollPaneSkin extends ScrollPaneSkin {

    private double prevVerticalBlockIncrement;
    private double prevHorizontalBlockIncrement;
    private double prevVerticalUnitIncrement;
    private double prevHorizontalUnitIncrement;

    public UnitIncrementScrollPaneSkin(final ScrollPane scrollPane) {
        super(scrollPane);
        filterScrollEvents();
    }

    @Override
    protected void layoutChildren(final double x, final double y, final double w, final double h) {
        prevVerticalUnitIncrement = getVerticalScrollBar().getUnitIncrement();
        prevHorizontalUnitIncrement = getHorizontalScrollBar().getUnitIncrement();
        prevVerticalBlockIncrement = getVerticalScrollBar().getBlockIncrement();
        prevHorizontalBlockIncrement = getHorizontalScrollBar().getBlockIncrement();
        super.layoutChildren(x, y, w, h);
        getVerticalScrollBar().setUnitIncrement(prevVerticalUnitIncrement);
        getHorizontalScrollBar().setUnitIncrement(prevHorizontalUnitIncrement);
        getVerticalScrollBar().setBlockIncrement(prevVerticalBlockIncrement);
        getHorizontalScrollBar().setBlockIncrement(prevHorizontalBlockIncrement);
    }

    private void filterScrollEvents() {
        getSkinnable().addEventFilter(ScrollEvent.SCROLL, event -> {

            if (event.getDeltaX() < 0) {
                getHorizontalScrollBar().increment();
            } else if (event.getDeltaX() > 0) {
                getHorizontalScrollBar().decrement();
            }

            if (event.getDeltaY() < 0) {
                getVerticalScrollBar().increment();
            } else if (event.getDeltaY() > 0) {
                getVerticalScrollBar().decrement();
            }

            event.consume();
        });
    }

    public ScrollBar getVerticalScrollBar() {
        return vsb;
    }

    public ScrollBar getHorizontalScrollBar() {
        return hsb;
    }
}

Usage:

@FXML
public void initialize() {
    final double initialInc = 0.2;
    UnitIncrementScrollPaneSkin skin = new UnitIncrementScrollPaneSkin(scrollPane);
    skin.getVerticalScrollBar().setUnitIncrement(initialInc);
    skin.getVerticalScrollBar().setBlockIncrement(initialInc);
    skin.getHorizontalScrollBar().setUnitIncrement(initialInc);
    skin.getHorizontalScrollBar().setBlockIncrement(initialInc);
    scrollPane.setSkin(skin);
}

Test App:

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="226.0" prefWidth="215.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.stackoverflow.app.scrollpane.SpeedUpScrollPaneController">
   <children>
      <ScrollPane fx:id="scrollPane" fitToHeight="true" fitToWidth="true" hbarPolicy="ALWAYS" prefHeight="200.0" prefWidth="200.0" vbarPolicy="ALWAYS">
         <content>
            <GridPane>
              <rowConstraints>
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              </rowConstraints>
            </GridPane>
         </content>
      </ScrollPane>
      <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
         <children>
            <Label text="V:" />
            <Spinner fx:id="spinnerVertical" prefHeight="25.0" prefWidth="85.0">
                 <valueFactory>
                    <SpinnerValueFactory.DoubleSpinnerValueFactory amountToStepBy="0.1" max="1.0" min="0" />
                 </valueFactory>
            </Spinner>
         </children>
      </HBox>
      <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
         <children>
            <Label text="H:" />
            <Spinner fx:id="spinnerHorizontal" prefHeight="25.0" prefWidth="85.0">
                <valueFactory>
                    <SpinnerValueFactory.DoubleSpinnerValueFactory amountToStepBy="0.1" max="1.0" min="0" />
                 </valueFactory>
            </Spinner>
         </children>
      </HBox>
      <ListView fx:id="listViewLog" prefHeight="200.0" prefWidth="200.0" />
   </children>
</VBox>
public class SpeedUpScrollPaneController {

    @FXML
    private ListView<String> listViewLog;
    @FXML
    private Spinner<Double> spinnerVertical;
    @FXML
    private Spinner<Double> spinnerHorizontal;
    @FXML
    private ScrollPane scrollPane;

    @FXML
    public void initialize() {
        mockScrollPane();
        initScrollPane();
    }

    private void mockScrollPane() {
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < 100; j++) {
                final Label label = new Label(i + "," + j);
                label.setMinWidth(60);
                label.setMinHeight(30);
                ((GridPane) scrollPane.getContent()).add(label, i, j);
            }
        }
    }

    private void initScrollPane() {
        final UnitIncrementScrollPaneSkin skin = new UnitIncrementScrollPaneSkin(scrollPane);
        scrollPane.setSkin(skin);
        initIncrement(0.2, spinnerHorizontal, skin.getHorizontalScrollBar());
        initIncrement(0.2, spinnerVertical, skin.getVerticalScrollBar());
    }

    private void initIncrement(final double increment, final Spinner<Double> spinner, final ScrollBar scrollBar) {
        scrollBar.unitIncrementProperty().addListener((source, prev, curr) -> {
            final String vLog = String.format("%tr: %s unitIncrement=%f", Calendar.getInstance(),
                    scrollBar.getOrientation(), curr);
            listViewLog.getItems().add(vLog);
            listViewLog.scrollTo(vLog);
        });
        scrollBar.blockIncrementProperty().addListener((source, prev, curr) -> {
            final String vLog = String.format("%tr: %s blockIncrement=%f", Calendar.getInstance(),
                    scrollBar.getOrientation(), curr);
            listViewLog.getItems().add(vLog);
            listViewLog.scrollTo(vLog);
        });
        spinner.valueProperty().addListener((source, prev, curr) -> {
            scrollBar.setUnitIncrement(curr);
            scrollBar.setBlockIncrement(curr);

        });

        Platform.runLater(() -> {
            scrollBar.setUnitIncrement(increment);
            scrollBar.setBlockIncrement(increment);
            spinner.getValueFactory().setValue(scrollBar.getUnitIncrement());
        });
    }
}
  • 0.0 -> no scrolling
  • 0.1 -> scrolling on 10%
  • 0.5 -> scrolling on 50%
  • 1.0 -> scrolling on 100%
  • Screencast

PS. If you have a lot of objects to be scrolled you could try the approach:

  • Create image bitmap representing scroll pane content
  • Show image bitmap on start scrolling
  • Move image at X,Y (based on ScrollEvent.getDeltaX(), ScrollEvent.getDeltaY())
  • Hide image, show scroll pane nodes with new coordinates on finish scrolling
Oleks
  • 1,011
  • 1
  • 14
  • 25
  • 1
    Like he said it will only work if you click on the Up and Down buttons(At the top and bottom of the scroll bar) and since you didn't clarify in your question that you needed the scroll wheel or buttons sped up you can't assume people would know that – Matt Jun 27 '19 at 20:22
  • @Petr M Let's clarify what speed up means. In terms of your question (as far as I understand) it means changing increment\decrement step. I added blockIncrement in case if the track of the bar is clicked (answer is updated). Maybe are you talking abt 'speed up' on dragging action? Could you pls check new blockIncrement implementation and describe the term speed-up in more details. – Oleks Jun 27 '19 at 21:34
  • Wha I thought was that when i scroll using scroll wheel the scroll bar will move faster but when I tried to implement your solution I really did not see that happening – Petr M Jun 29 '19 at 08:54
  • @Petr M Please take a look at filterScrollEvents method. Scrolling using mouse wheel will move the scroll bar as fast as Up and Down buttons. – Oleks Jul 01 '19 at 15:13