1

I can listen for scroll events:

tableView.addEventFilter(javafx.scene.input.ScrollEvent.SCROLL,
        new EventHandler<javafx.scene.input.ScrollEvent>() {
            @Override
            public void handle(final javafx.scene.input.ScrollEvent scrollEvent) {
            System.out.println("Scrolled.");

            }
        });

But How can I be notified if the bottom/ top of the table is reached?

kerner1000
  • 3,382
  • 1
  • 37
  • 57

3 Answers3

4

A simple way of doing this is to retrieve the ScrollBar using a lookup:

ScrollBar tvScrollBar = (ScrollBar) tableView.lookup(".scroll-bar:vertical");

You can then add a listener to check if it's reached the bottom (the valueProperty of the ScrollBar is represented by the percentage it's been scrolled, so 0.0 is the top and 1.0 is the bottom):

tvScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
    if ((Double) newValue == 1.0) {
        System.out.println("Bottom!");
    }
});

Below is a simple MCVE that demonstrates how to put it all together:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ScrollBarNotify extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple interface
        VBox root = new VBox(5);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // Simple TableView to demonstrate
        TableView<String> tableView = new TableView<>();
        TableColumn<String, String> column = new TableColumn<>("Text");
        column.setCellValueFactory(f -> new SimpleStringProperty(f.getValue()));

        tableView.getColumns().add(column);

        // Add some sample items to our TableView
        for (int i = 0; i < 100; i++) {
            tableView.getItems().add("Item #" + i);
        }

        // Now, let's add a listener to the TableView's scrollbar. We can only access the ScrollBar after the Scene is
        // rendered, so we need to do schedule this to run later.
        Platform.runLater(() -> {
            ScrollBar tvScrollBar = (ScrollBar) tableView.lookup(".scroll-bar:vertical");
            tvScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
                if ((Double) newValue == 1.0) {
                    System.out.println("Bottom!");
                }
            });

        });

        // Finally, add the TableViewto our layout
        root.getChildren().add(tableView);

        // Show the Stage
        primaryStage.setWidth(300);
        primaryStage.setHeight(300);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}
Zephyr
  • 9,885
  • 4
  • 28
  • 63
3

If you're using Java 10+ you can subclass TableViewSkin and get access to the VirtualFlow. The latter class has the position property which you can use to know if the top or bottom has been reached.

Here's an example using custom events:

MyEvent.java

import javafx.event.Event;
import javafx.event.EventType;

public class MyEvent extends Event {

  public static final EventType<MyEvent> ANY = new EventType<>(Event.ANY, "MY_EVENT");
  public static final EventType<MyEvent> TOP_REACHED = new EventType<>(ANY, "TOP_REACHED");
  public static final EventType<MyEvent> BOTTOM_REACHED = new EventType<>(ANY, "BOTTOM_REACHED");

  public MyEvent(EventType<? extends MyEvent> eventType) {
    super(eventType);
  }

}

MyTableViewSkin.java

import javafx.scene.control.TableView;
import javafx.scene.control.skin.TableViewSkin;

public class MyTableViewSkin<T> extends TableViewSkin<T> {

  public MyTableViewSkin(TableView<T> control) {
    super(control);
    getVirtualFlow().positionProperty().addListener((obs, oldVal, newVal) -> {
      if (newVal.doubleValue() == 0.0) {
        control.fireEvent(new MyEvent(MyEvent.TOP_REACHED));
      } else if (newVal.doubleValue() == 1.0) {
        control.fireEvent(new MyEvent(MyEvent.BOTTOM_REACHED));
      }
    });
  }

}

App.java

import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) {
    var table = new TableView<Integer>();
    for (int i = 0; i < 250; i++) {
      table.getItems().add(i);
    }

    var column = new TableColumn<Integer, Number>("Value");
    column.setCellValueFactory(features -> new SimpleIntegerProperty(features.getValue()));
    table.getColumns().add(column);

    table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    table.setSkin(new MyTableViewSkin<>(table));
    table.addEventHandler(MyEvent.ANY, event -> System.out.printf("%s%n", event.getEventType()));

    primaryStage.setScene(new Scene(table, 500, 300));
    primaryStage.setTitle("Example");
    primaryStage.show();
  }

}

In this example I manually call table.setSkin. Another option is to subclass TableView and override createDefaultSkin which returns the skin you want to use.

Slaw
  • 37,820
  • 8
  • 53
  • 80
-1
tableView.addEventFilter(javafx.scene.input.ScrollEvent.SCROLL,
        new EventHandler<javafx.scene.input.ScrollEvent>() {
            @Override
            public void handle(final javafx.scene.input.ScrollEvent scrollEvent) {

                Object virtualFlow =  ((javafx.scene.control.SkinBase<?>) tableView.getSkin()).getChildren().get(1);
                double position = -1;
                try {
                    position = (double) virtualFlow.getClass().getMethod("getPosition").invoke(virtualFlow);
                } catch (Exception ignored) { }

                if(position == 0.0) {
                    System.out.println("scrolled to top!");
                }
                else if(position == 1.0) {
                    System.out.println("scrolled to bottom!");
                }

            }
        });
guleryuz
  • 2,714
  • 1
  • 15
  • 19