1

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(); });
        }
alex
  • 5,516
  • 2
  • 36
  • 60

2 Answers2

1

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.

eckig
  • 10,964
  • 4
  • 38
  • 52
  • Although it is possible to use reflection, but its not the only way of doing so. I have provided another easy answer below. – Amin Sep 03 '16 at 07:36
1

Here is a simple way of doing so without doing any reflection tricks:

First you should implement your own extension of ListViewSkin, say, CustomListViewSkin. You will set the skin on the listview using .setSkin method later. (You may extend ListView and override the createDefaultSkin method to have your customized skin in action as well). Now, you should extend VirtualFlow (say CustomVirtualFlow) and let CustomListViewSkin create an instance of CustomVirtualFlow at runtime by overriding the createVirtualFlow of CustomVirtualFlow class. Then, you should add this methods to CustomVirtualFlow to finish the job.

import com.sun.javafx.scene.control.skin.VirtualFlow;
import com.sun.javafx.scene.control.skin.VirtualScrollBar;
import javafx.scene.control.ListCell;

public class CustomVirtualFlow<T> extends VirtualFlow<ListCell<T>>
{

   public VirtualScrollBar getVerticalBar()
   {
       return getVbar();
   }

   public VirtualScrollBar getHorizontalBar()
   {
       return getHbar();
   }

}

Finally, add this method to your CustomListViewSkin implementation:

public CustomVirtualFlow<ListCell<T>> getFlow()
{
    return (CustomVirtualFlow<ListCell<T>>) flow;
}

And, now:

ListView lv = new ListView();
CustomListViewSkin skin = new CustomListViewSkin(lv);
lv.setSkin(skin);

//add the list view to some scene

CustomVirtualFlow flow = skin.getFlow();
ScrollBar vb = flow.getVerticalBar();
//do whatever you like with the vb instance now
Gil Matzov
  • 213
  • 2
  • 12
Amin
  • 292
  • 2
  • 11
  • Or you look at this [answer of mine](http://stackoverflow.com/questions/26537548/javafx-listview-with-touch-events-for-scrolling-up-and-down/39304755#39304755) – Amin Sep 03 '16 at 08:14