获取 MouseEvent 以定位 TilePane 内的 ImageView

Getting a MouseEvent to target ImageViews inside a TilePane

我在 TilePane 中有很多 ImageView,在 StackPane 中,然后在 ScrollPane 中。 TilePane 的子项之间没有边框、填充或边距,因此我不可能不单击 ImageView。当我单击图像时,我希望 MouseEvent 的目标是 ImageViews,但它是 TilePane。

如何让事件链在 ImageView 上结束而不是在 TilePane 上提前结束?

否则,有什么方法可以使用其他信息获取ImageView吗?也许使用事件的坐标?

我通常的做法是在我感兴趣的节点上注册鼠标监听器;在您的情况下,这意味着为每个 ImageView 注册一个鼠标侦听器。然后很容易让每个鼠标侦听器都引用它注册的特定图像视图,或者如果需要的话引用其他数据(例如文件名)。

可能会发生一件事:如果您的图像具有透明像素,则在图像的该部分上单击鼠标将默认 "drop through" 到下面的节点。您可以通过在图像视图上调用 imageView.setPickOnBounds(true); 来更改此行为。

一些测试代码。如果你 运行 这你会看到一些带有不同颜色背景的编号图像。大约四分之一的人有透明背景(他们看起来是白色的)。如果您单击这些(但不是数字的实际文本),您将看到注册到滚动窗格和堆栈窗格的鼠标处理程序将平铺窗格作为目标,并且处理程序注册到 ImageView 甚至没有被调用。对于那些没有透明背景的人,目标始终是 ImageView。如果您 select 复选框,那么 pickOnBounds 对于所有 ImageView 都是 true,透明和不透明图像都可以按照您的意愿运行。

import java.util.Random;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class ImageViewClickTest extends Application {

    private static final Random RNG = new Random();

    @Override
    public void start(Stage primaryStage) {
        TilePane tilePane = new TilePane();

        CheckBox pickOnBounds = new CheckBox("Pick on bounds");
        pickOnBounds.setPadding(new Insets(16));

        for (int i=1; i<=200; i++) {
            ImageView imageView = createImageView(i);

            imageView.pickOnBoundsProperty().bind(pickOnBounds.selectedProperty());

            // mouse handler directly on image view:
            // can access image-view specific data...
            String message = "Clicked on Image "+i ;
            imageView.setOnMouseClicked(e -> 
                System.out.println("From handler on ImageView: "+message));


            tilePane.getChildren().add(imageView);
        }
        StackPane stack = new StackPane(tilePane);

        stack.setOnMouseClicked(e -> {
            // source will be the stack pane
            // target will be the top-most node 
            // (i.e. the ImageView, in most cases)
            System.out.println("From handler on stack pane: Source: "+e.getSource());
            System.out.println("From handler on stack pane: Target: "+e.getTarget());
        });


        ScrollPane scroller = new ScrollPane(stack);
        scroller.setFitToWidth(true);

        scroller.setOnMouseClicked(e -> {
            // source will be the scroll pane
            // target will be the top-most node 
            // (i.e. the ImageView, in most cases)
            System.out.println("From handler on scroller: Source: "+e.getSource());
            System.out.println("From handler on scroller: Target: "+e.getTarget());
        });

        BorderPane root = new BorderPane(scroller, pickOnBounds, null, null, null);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private ImageView createImageView(int index) {
        Label label = new Label(Integer.toString(index));
        label.setAlignment(Pos.CENTER);
        label.setMinSize(48, 48);
        label.setStyle(randomStyle());
        Image image = new Scene(label, Color.TRANSPARENT).snapshot(null);
        ImageView imageView = new ImageView(image);
        return imageView ;
    }

    private String randomStyle() {
        StringBuilder style = new StringBuilder();
        style.append("-fx-background-color: -fx-background;");
        style.append("-fx-background: ");
        if (RNG.nextDouble() < 0.25) {
            style.append( "transparent;");
            style.append(" -fx-text-fill: black;") ;
        } else {
            String bg = String.format("#%02x%02x%02x;", 
                    RNG.nextInt(256), RNG.nextInt(256), RNG.nextInt(256));
            style.append(bg);
        }
        return style.toString();
    }

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