2

I am using a controlsfx GridView in a JavaFX application. It shows a scrollbar when needed, but I can't find any way to determine where the scrollbar is positioned at, nor update it. I need to be able to do things like respond to a "go to the top" command from the user and scroll up; or scroll to keep the selected thumbnail visible as the user uses arrow keys to navigate through the grid. But I don't see how to get access to the current scroll position, nor manipulate it, as you can with a ScrollPane.

For example, here is a sample application that creates a GridView with 100 generated images in it. I add a listener to the "onScrollProperty", but it is never called. I also have no idea how I would cause it to scroll to a certain scroll position (0..1):

import java.awt.image.BufferedImage;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import org.controlsfx.control.GridView;
import org.controlsfx.control.cell.ImageGridCell;

// Demo class to illustrate the slowdown problem without worrying about thumbnail generation or fetching.
public class GridViewDemo extends Application {
  private static final int CELL_SIZE = 200;

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

  public void start(Stage primaryStage) {
    // Create a Scene with a ScrollPane that contains a TilePane.
    GridView<Image> gridView = new GridView<>();
    gridView.setCellFactory(gridView1 -> new ImageGridCell());
    gridView.setCellWidth(CELL_SIZE);
    gridView.setCellHeight(CELL_SIZE);
    gridView.setHorizontalCellSpacing(10);
    gridView.setVerticalCellSpacing(10);
    addImagesToGrid(gridView);

    gridView.onScrollProperty().addListener((observable, oldValue, newValue) -> {
      // Never called
      System.out.println("Scrolled...");
    });

    primaryStage.setScene(new Scene(gridView, 1000, 600));
    primaryStage.setOnCloseRequest(x -> {
      Platform.exit();
      System.exit(0);
    });
    primaryStage.show();
  }

  private void addImagesToGrid(GridView<Image> gridView) {
    for (int i = 0; i < 100; i++) {
      final Image image = createFakeImage(i, CELL_SIZE);
      gridView.getItems().add(image);
    }
  }

  // Create an image with a bunch of rectangles in it just to have something to display.
  private static Image createFakeImage(int imageIndex, int size) {
    BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
    Graphics g = image.getGraphics();
    for (int i = 1; i < size; i ++) {
      g.setColor(new Color(i * imageIndex % 256, i * 2 * (imageIndex + 40) % 256, i * 3 * (imageIndex + 60) % 256));
      g.drawRect(i, i, size - i * 2, size - i * 2);
    }
    return SwingFXUtils.toFXImage(image, null);
  }
}

The needed maven include is:

    <dependency>
      <groupId>org.controlsfx</groupId>
      <artifactId>controlsfx</artifactId>
      <version>8.0.6_20</version>
    </dependency>

Here is a screen shot of what it looks like.

GridView with scrollbar

user3763100
  • 13,037
  • 3
  • 13
  • 9

1 Answers1

3

Maybe to late for the questioner. But after i stumbled across the same problem, after hours of investigation, hopefully useful to others...

I add a listener to the "onScrollProperty", but it is never called.

This "works as intended". :-/ See https://bugs.openjdk.java.net/browse/JDK-8096847. You have to use "addEventFilter()". See example below.

To set the scroll position is a pain. You have to get the underlying "VirtualFlow" object of the GridView. VirtualFlow contains methods to set the scroll position to specific rows. It's strange that GridView seems to have no API for this common use case. :-(

A "prove of concept" example how to set scroll position, for a GridView with images:

/**
 * Register a scroll event and a key event listener to gridView. 
 * After that navigate with "arrow up" and "arrow down" the grid.
 * @param gridView 
 */
private void addScrollAndKeyhandler(final GridView<Image> gridView) {
    // example for scroll listener
    gridView.addEventFilter(ScrollEvent.ANY, e-> System.out.println("*** scroll event fired ***"));
    
    // add UP and DOWN arrow key listener, to set scroll position 
    gridView.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
        if (e.getCode() == KeyCode.UP) oneRowUp(gridView);
        if (e.getCode() == KeyCode.DOWN) oneRowDown(gridView);
    });
}

int selectedRow = 0; // current "selected" GridView row.
/**
 * Scrolls the GridView one row up.
 * @param gridView
 */
private void oneRowUp(final GridView<Image> gridView) {
    // get the underlying VirtualFlow object
    VirtualFlow<?> flow =  (VirtualFlow<?>) ((GridViewSkin<?>) gridView.getSkin()).getChildren().get(0);
    if (flow.getCellCount() == 0) return; // check that rows exist
    
    if (--selectedRow < 0) selectedRow = 0;
    if (selectedRow >= flow.cellCountProperty().get()) selectedRow =  flow.getCellCount() - 1;
    System.out.println("*** KeyEvent oneRowUp " + selectedRow + " ***"); 
    
    flow.scrollTo(selectedRow);
}

/**
 * Scrolls the GridView one row down.
 * @param gridView
 */
private void oneRowDown(final GridView<Image> gridView) {
    // get the underlying VirtualFlow object
    VirtualFlow<?> flow =  (VirtualFlow<?>) ((GridViewSkin<?>) gridView.getSkin()).getChildren().get(0);
    if (flow.getCellCount() == 0) return; // check that rows exist
    
    if (++selectedRow >= flow.cellCountProperty().get()) selectedRow =  flow.getCellCount() - 1;
    System.out.println("*** KeyEvent oneRowDown " + selectedRow + " ***");
    
    flow.scrollTo(selectedRow);
}

(Tested with javafx 15.0.1 and controlsfx 11.0.3)

VinZ
  • 414
  • 7
  • 15