Search code examples
listviewscrolljavafxfxml

JavaFX ScrollListener ListView


I'm created the following example containing a ListView with a registered scroll event listener. Unfortunately the listener does only fire when scrolling via mouse wheel, but not when using the scrollbar on the side. Whats is wrong with the code?

Controller.java

package sample;

import java.net.URL;
import java.util.Arrays;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;

public class Controller implements Initializable {
    @FXML
    private ListView lstPreview;

    private ObservableList items = FXCollections.observableArrayList();

    public Controller() {
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        items.addAll(Arrays.asList("Test 1", "Test 2", "Test 3", "Test 4", "Test 5", "Test 6", "Test 7", "Test 8", "Test 9", "Test 10"));
        lstPreview.setItems(items);
        lstPreview.addEventFilter(javafx.scene.input.ScrollEvent.ANY, event -> { System.out.println("ScrollEvent!"); });
        lstPreview.addEventFilter(javafx.scene.control.ScrollToEvent.ANY, event -> { System.out.println("ScrollToEvent!"); });

    }
}

Main.java

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();
    }


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

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import java.net.URL?>

<GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <stylesheets>
      <URL value="@style.css" />
   </stylesheets>
   <columnConstraints>
      <ColumnConstraints />
      <ColumnConstraints hgrow="ALWAYS" />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
      <RowConstraints vgrow="ALWAYS" />
   </rowConstraints>
   <children>
      <ListView fx:id="lstPreview" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.vgrow="ALWAYS" />
   </children>
</GridPane>

Solution

I ended up using @Uluk Biy solution, maybe one day the Java 8 JDK gets a function like this:

/**
 * Looks up the first found scrollbar of a given control matching the desired orientation. If none found, null is returned
 *
 * @param control
 * @param orientation
 * @return
 */
public static javafx.scene.control.ScrollBar getScrollbarComponent(javafx.scene.control.Control control, javafx.geometry.Orientation orientation) {
    javafx.scene.Node n = control.lookup(".scroll-bar");
    if (n instanceof javafx.scene.control.ScrollBar) {
        final javafx.scene.control.ScrollBar bar = (javafx.scene.control.ScrollBar) n;
        if (bar.getOrientation().equals(orientation)) {
            return bar;
        }
    }
    return null;
}

Using it like this:

javafx.scene.control.ScrollBar timelineBar = de.elara.asgard.util.FxUtils.getScrollbarComponent(listTimeline, javafx.geometry.Orientation.HORIZONTAL);
        if (timelineBar != null) {
            timelineBar.addEventFilter(ScrollToEvent.ANY, event -> { doScrollingTimeline(); });
        }

Solution

  • According to the Documentation:

    Scroll event indicates that user performed scrolling by mouse wheel, track pad, touch screen or other similar device.

    So it has nothing to do with the ScrollBars of the ListView. As they are implementation detail it is not so easy to acces them, besides doing some reflection tricks.