JavaFX GridView, как получить идентификаторы видимых элементов

Я пытаюсь получить текущие видимые индексы ячеек/строк GridView, заполненные изображениями.

Я нашел способ извлечь идентификатор из слушателя, но когда я начал добавлять текст/метки поверх изображения и помещать его как StackPane в GridView вместо Images, он перестал работать (исключение приведения класса с IndexedCell).

Вот основной класс:

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);
    }


} 

Вот MyGridView класс:


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);
    }
}

Если вы переключите функцию setCellFactoryWorking() на setCellFactoryNotWorking(), вы увидите, что ссылочный класс, на который наведена мышь, изменился, и больше не существует IndexedCell с идентификаторами. На самом деле это плохой метод обнаружения реальных видимых элементов GridView. Есть ли что-нибудь более общее, что не зависит от типов классов элементов ячейки и просто может сказать:

  • видимые идентификаторы строк

  • видимые идентификаторы ячеек

  • если щелкнуть ячейку мышью, то это тоже идентификатор

  • также было бы неплохо узнать идентификатор ячейки, просто наведя на нее курсор мыши

Есть обходной путь для сканирования родителей, но я думаю, что у него есть более элегантное решение?

Ariloum 05.07.2024 00:02

Почему вы регистрируете обработчики событий в самом представлении сетки? Почему бы просто не зарегистрировать обработчики событий в ячейках? Затем вы можете легко получить данные, отображаемые в ячейке.

James_D 06.07.2024 01:15

Я пытался сделать одну простую функцию прокрутки вместо трех (одну для ячеек, другую между ячейками и третью — полосу прокрутки), чтобы упростить код и избежать каскадного воссоздания множества прослушивателей для каждой ячейки при событиях прокрутки. Также в моих ячейках будет несколько разных объектов, таких как текст и символы — и это еще один слушатель? Этот предпочитает более общие решения.

Ariloum 06.07.2024 22:28
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во время поиска я не нашел ничего, что указывало бы на то, что вы можете получить идентификатор строки или идентификатор столбца. Как вы знаете, вы можете получить индекс. Я не понимаю, почему у вас возникли ошибки при добавлении Labels и Images\ImageViews к StackPane. Вот пример, который выдает идентификатор и индекс ячейки при входе, выходе или щелчке. У вас есть модель, представляющая одну ячейку? В этом примере используется модель с изображением и строкой для представления ячейки.

Основной

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;
    }
}

Модель изображения

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;
    }    
}

Выход

Я не опубликовал выходной GIF-файл, потому что StackOverflow уменьшил принятый размер изображения до 50 мегапикселей.

Это хорошо, решает многие вещи, которые мне не хватает. Я не знал, что вы можете получить идентификатор ячейки из события с помощью getIndex. На самом деле я использую BorderPane в своих ячейках, чтобы добавить текст и графику сверху и снизу, а изображение становится фоном. Также я нашел способ получить первые/последние строки без событий, я делаю это с помощью этого кода: VirtualFlow<?> flow = (VirtualFlow<?>) ((GridViewSkin<?>)gridView.getSkin()).getChildren ().получить(0); flow.getFirstVisibleCell().getIndex(); //возвращает первый видимый GridRow, то же самое для последнего видимого.

Ariloum 06.07.2024 15:55

Другие вопросы по теме