javajavafxgridviewcontrolsfx

JavaFX GridView how to get visible elements id's


I'm trying to get the current visible cells/rows indexes of the GridView filled with images.

I found the way with taking the id's out of the listener but when I started adding text/labels over the image and putting it as StackPane to the GridView instead of Images it stopped working (class cast exception with the IndexedCell).

Here is main class:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.stage.Stage;


public class MainApp extends Application {
    public MyGridView myGridView;


    @Override
    public void start(final Stage primaryStage) {

        myGridView = new MyGridView();

        HBox row1 = new HBox(myGridView);
        HBox.setHgrow(myGridView, Priority.ALWAYS);

        Scene scene = new Scene(row1, 1200, 800, Color.BLACK);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

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


} 

Here is MyGridView class:


import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.IndexedCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import org.controlsfx.control.GridCell;
import org.controlsfx.control.GridView;
import org.controlsfx.control.cell.ImageGridCell;

import java.awt.*;
import java.awt.image.BufferedImage;



public class MyGridView extends GridView {
    public static final int GRID_CELL_WIDTH = 512;
    public static final int GRID_CELL_HEIGHT = 360;
    public static final int GRID_CELL_SPACING_HORIZONTAL = 5;
    public static final int GRID_CELL_SPACING_VERTICAL = 5;

    public MyGridView() {
        setCellWidth(GRID_CELL_WIDTH);
        setCellHeight(GRID_CELL_HEIGHT);
        setHorizontalCellSpacing(GRID_CELL_SPACING_HORIZONTAL);
        setVerticalCellSpacing(GRID_CELL_SPACING_VERTICAL);

        addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
            Node clickedNode = e.getPickResult().getIntersectedNode();
            IndexedCell index = ((IndexedCell) clickedNode);
            int clickedItemId = index.getIndex();
            System.out.println("Clicked id = " + clickedItemId);
        });

        addEventFilter(ScrollEvent.ANY, e -> {
            Node clickedNode = e.getPickResult().getIntersectedNode();
            System.out.println(clickedNode.getClass());
            IndexedCell index = ((IndexedCell) clickedNode);
            int clickedItemId = index.getIndex();
            System.out.println("Scrolled id = " + clickedItemId);
        });


        //scroll/click cells is working with this, comment it and uncomment next one
        setCellFactoryWorking();
//        setCellFactoryNotWorking();

        addImagesToGrid(this);

    }

    private void setCellFactoryWorking() {
        setCellFactory(gv -> new GridCell<Image>() {
            private final ImageView imageView = new ImageView();

            {
                imageView.setFitHeight(GRID_CELL_HEIGHT);
                imageView.setPreserveRatio(true);
            }

            @Override
            protected void updateItem(Image image, boolean empty) {
                super.updateItem(image, empty);
                imageView.setImage(image);
                setGraphic(imageView);

            }
        });
    }

    private void setCellFactoryNotWorking() {
        setCellFactory(gv -> new GridCell<Image>() {
            private final ImageView imageView = new ImageView();

            {
                imageView.setFitHeight(GRID_CELL_HEIGHT);
                imageView.setPreserveRatio(true);
            }

            @Override
            protected void updateItem(Image image, boolean empty) {
                super.updateItem(image, empty);
                imageView.setImage(image);
                Text text = new Text("  Lorem Ipsum");
                text.setFill(javafx.scene.paint.Color.rgb(255, 255, 255));
                StackPane pane = new StackPane();
                pane.getChildren().add(imageView);
                pane.getChildren().add(text);
                pane.setAlignment(Pos.TOP_LEFT);
                setGraphic(pane);

            }
        });
    }

    private void addImagesToGrid(GridView<Image> gridView) {
        for (int i = 1; i < 200; i++) {
            final Image image = createFakeImage(i, GRID_CELL_WIDTH, GRID_CELL_HEIGHT);
            gridView.getItems().add(image);
        }
    }


    private static Image createFakeImage(int imageIndex, int width, int height) {
        BufferedImage image = new BufferedImage(width, width, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        for (int i = 1; i < width; i++) {
            g.setColor(new Color(i * imageIndex % 256, i * 2 * (imageIndex + 40) % 256, i * 3 * (imageIndex + 60) % 256));
            double proportion = width / height;
            g.drawRect(i, i, width - i * 2, (int) ((width - i * 2) * proportion));
        }


        return SwingFXUtils.toFXImage(image, null);
    }
}

If you switch setCellFactoryWorking() func to setCellFactoryNotWorking() you will see that reference class that mouse is hovering changed and there is no more IndexedCell exists with id's. Actually that is kinda bad method detecting actual visible elements of the GridView, is there anything more generic that is not dependent on the cell element class types and just could tell:


Solution

  • I did not see anything during my search that would suggest you can get the row ID or the column ID. You can get the index, as you know. I am not sure why you had errors when adding Labels and Images\ImageViews to a StackPane. Here is an example that will give the ID and index of a cell when entered, exited, or clicked. Do you have a model that represents one cell? This example uses a model with an Image and a String to represent the cell.

    Main

    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ThreadLocalRandom;
    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    import javafx.scene.image.PixelWriter;
    import javafx.scene.image.WritableImage;
    import javafx.scene.paint.Color;
    import org.controlsfx.control.GridCell;
    import org.controlsfx.control.GridView;
    
    public class App extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        public static final int GRID_CELL_WIDTH = 50;
        public static final int GRID_CELL_HEIGHT = 50;
        public static final int GRID_CELL_SPACING_HORIZONTAL = 5;
        public static final int GRID_CELL_SPACING_VERTICAL = 5;
        
        List<Color> defaultColors = getAllDefaultColors();
        
        @Override
        public void start(Stage primaryStage) {    
            ObservableList<ImageModel> modelList = FXCollections.observableArrayList();
            for(int i = 1; i <= 1000; i++)
            {
                modelList.add(new ImageModel(i, createFakeImage(GRID_CELL_WIDTH, GRID_CELL_HEIGHT)));
            }
            
            GridView<ImageModel> gridView = new GridView();
            gridView.setCellWidth(GRID_CELL_WIDTH);
            gridView.setCellHeight(GRID_CELL_HEIGHT);
            gridView.setHorizontalCellSpacing(GRID_CELL_SPACING_HORIZONTAL);
            gridView.setVerticalCellSpacing(GRID_CELL_SPACING_VERTICAL);
            
            gridView.setItems(modelList);
            gridView.setCellFactory((GridView<ImageModel> param) -> {
                GridCell<ImageModel> cell = new GridCell<ImageModel>() {                
                    ImageView imageView = new ImageView();
                    Label label = new Label();
                    StackPane spRoot = new StackPane(imageView, label);
                    
                    @Override
                    protected void updateItem(ImageModel item, boolean empty) {
                        super.updateItem(item, empty);
                        if (item != null) {
                            label.setText(Integer.toString(item.getId()));
                            imageView.setImage(item.getImage());                          
                            
                            setOnMouseClicked((mouseEvent) -> {                            
                                System.out.println("Clicked: index-" + getIndex() + "\tID-" + item.getId());
                                mouseEvent.consume();
                            });
                            
                            setOnMouseEntered((mouseEvent) -> {                            
                                System.out.println("Entered: index-" + getIndex() + "\tID-" + item.getId());
                                mouseEvent.consume();
                            });
                            
                            setOnMouseExited((mouseEvent) -> {                            
                                System.out.println("Exited: index-" + getIndex() + "\tID-" + item.getId());
                                mouseEvent.consume();
                            });
                            
                            setGraphic(spRoot);
                        } else {
                            setText("");
                            setGraphic(null);
                        }
                    }
                };
                
                return cell;
            });
            
            StackPane root = new StackPane(gridView);
            Scene scene = new Scene(root, 1080, 720);
            primaryStage.setScene(scene);
            primaryStage.setTitle("JavaFX App");
            primaryStage.show();
        }    
        
        private Image createFakeImage(int width, int height) {
            Color currentColor = defaultColors.get(ThreadLocalRandom.current().nextInt(defaultColors.size()));
            
            WritableImage writableImage = new WritableImage(width, height);
            PixelWriter pixelWriter = writableImage.getPixelWriter();
    
            for(int x = 0; x < width; x++)
            {
                for(int y = 0; y < height; y++)
                {
                    pixelWriter.setColor(x, y, currentColor);
                }
            }
            
            return writableImage;
        }
        
        private List<Color> getAllDefaultColors(){
            List<Color> colors = new ArrayList<>();
            
            try 
            {
                
                Class clazz = Class.forName("javafx.scene.paint.Color");
                if (clazz != null) {
                    Field[] field = clazz.getFields();
                    for (Field f : field) {
                        Object obj = f.get(null);
                        if(obj instanceof Color){
                            colors.add((Color) obj);
                        }
                    }
                }
                
            } catch (ClassNotFoundException | IllegalArgumentException | IllegalAccessException ex) {
                System.out.println(ex.toString());
            } 
            
            return colors;
        }
    }
    

    ImageModel

    import javafx.scene.image.Image;
    
    /**
     *
     * @author sedj601
     */
    public class ImageModel {
        private int id;
        private Image image;
    
        public ImageModel(int id, Image image) {
            this.id = id;
            this.image = image;
        }
    
        public Image getImage() {
            return image;
        }
    
        public void setImage(Image image) {
            this.image = image;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }    
    }
    

    Output

    I did not post the output GIF because StackOverflow has reduced the accepted image size to 50 megapixels.