当前位置: 首页 > 知识库问答 >
问题:

将鼠标事件委托给JavaFX堆栈窗格中的所有子级

欧阳玺
2023-03-14

我试图想出一个解决方案,允许多个窗格节点在组装成StackPane时独立处理鼠标事件

StackPane
   Pane 1
   Pane 2
   Pane 3

我希望能够在每个子级中处理鼠标事件,并且第一个调用消费()的子级会停止将事件传递给下一个子级。

我也知道setPickOnBands(false),但这并不能解决所有情况,因为一些覆盖将基于Canvas的像素,即不涉及场景图。

我用Node.fireEvent()尝试了各种实验。然而,这些总是导致递归以堆栈溢出告终。这是因为事件是从根场景传播的,并再次触发相同的处理程序。

我要寻找的是一些方法来单独触发子窗格上的事件处理程序,而不让事件通过其正常路径。

到目前为止,我最好的解决方法是使用过滤器捕获事件并手动调用处理程序。我需要为MouseMove等重复此操作

parent.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
    for (Node each : parent.getChildren()) {
        if (!event.isConsumed()) {
            each.getOnMouseClicked().handle(event);
        }
    }
    event.consume();
});

然而,这只会触发使用setOnMouseClicked添加的侦听器,而不是addEventHandler,并且仅在该节点上,而不是子节点上。

另一种解决方案是接受JavaFX不是这样工作的,并像这样重组窗格,这将允许正常的事件传播发生。

Pane 1
   Pane 2
      Pane 3

示例

import javafx.application.Application;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class EventsInStackPane extends Application {

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

    private static class DebugPane extends Pane {
        public DebugPane(Color color, String name) {
            setBackground(new Background(new BackgroundFill(color, CornerRadii.EMPTY, Insets.EMPTY)));
            setOnMouseClicked(event -> {
                System.out.println("setOnMouseClicked " + name + " " + event);
            });
            addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
                System.out.println("addEventHandler " + name + " " + event);
            });
            addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
                System.out.println("addEventFilter " + name + " " + event);
            });
        }

    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        DebugPane red = new DebugPane(Color.RED, "red");
        DebugPane green = new DebugPane(Color.GREEN, "green");
        DebugPane blue = new DebugPane(Color.BLUE, "blue");
        setBounds(red, 0, 0, 400, 400);
        setBounds(green, 25, 25, 350, 350);
        setBounds(blue, 50, 50, 300, 300);

        StackPane parent = new StackPane(red, green, blue);
        eventHandling(parent);

        primaryStage.setScene(new Scene(parent));
        primaryStage.show();
    }

    private void eventHandling(StackPane parent) {
        parent.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
            if (!event.isConsumed()) {
                for (Node each : parent.getChildren()) {
                    Event copy = event.copyFor(event.getSource(), each);
                    parent.fireEvent(copy);
                    if (copy.isConsumed()) {
                        break;
                    }
                }
            }
            event.consume();
        });
    }
    private void setBounds(DebugPane panel, int x, int y, int width, int height) {
        panel.setLayoutX(x);
        panel.setLayoutY(y);
        panel.setPrefWidth(width);
        panel.setPrefHeight(height);
    }
}

共有1个答案

蔚弘量
2023-03-14

使用@jewelsea的提示,我可以使用自定义链。我从添加到堆栈窗格前面的“catcher”窗格中完成了此操作。然后,使用所有子项按相反顺序构建一个链,不包括自身。

private void eventHandling(StackPane parent) {
    Pane catcher = new Pane() {
        @Override
        public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
            EventDispatchChain chain = super.buildEventDispatchChain(tail);
            for (int i = parent.getChildren().size() - 1; i >= 0; i--) {
                Node child = parent.getChildren().get(i);
                if (child != this) {
                    chain = chain.prepend(child.getEventDispatcher());
                }
            }
            return chain;
        }
    };
    parent.getChildren().add(catcher);
}
 类似资料:
  • 我有以下代码来显示(自定义by-mvn repo) 问题是, > 我希望在鼠标输入时显示弹出窗口-工作正常。 我希望在用户从标签退出鼠标时隐藏弹出窗口,但在弹出窗口中输入鼠标时不隐藏弹出窗口。 我已经在标签上添加了鼠标插入和鼠标退出操作,但如何处理另一种情况,即如果用户输入鼠标进入弹出窗口,我不想隐藏弹出窗口。

  • 捕获和冒泡允许我们实现一种被称为 事件委托 的强大的事件处理模式。 这个想法是,如果我们有许多以类似方式处理的元素,那么就不必为每个元素分配一个处理程序 —— 而是将单个处理程序放在它们的共同祖先上。 在处理程序中,我们获取 event.target 以查看事件实际发生的位置并进行处理。 让我们看一个示例 —— 反映中国古代哲学的 八卦图。 如下所示:在新窗口中打开 在沙箱中打开 其 HTML 如

  • 您好,当鼠标单击stackpane(父)时,我试图取消选择listview项。我尝试了这段代码,但当用户单击按钮(Stackpane的子项)时,事件仍然被触发: 如何仅触发stackpane鼠标事件单击?

  • 主要内容:为什么要使用事件委托,事件委托实现原理,事件委托的优点,总结利用 JS 事件冒泡动态为元素绑定事件的方法称为事件委托(Event Delegation,也称为“事件代理”),是 JavaScript 中最热门的技术之一。 事件委托就是把原本需要绑定在子元素上的事件(onclick、onkeydown 等)委托给它的父元素,让父元素来监听子元素的冒泡事件,并在子元素发生事件冒泡时找到这个子元素。 举个简单的例子,整个宿舍的同学都需要去取快递,一种方法是让他们

  • 本文向大家介绍C#中的委托是什么?事件是不是一种委托?事件和委托的关系。相关面试题,主要包含被问及C#中的委托是什么?事件是不是一种委托?事件和委托的关系。时的应答技巧和注意事项,需要的朋友参考一下 委托可以把一个方法作为参数代入另一个方法。 委托可以理解为指向一个函数的指针。 委托和事件没有可比性,因为委托是类型,事件是对象,下面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实

  • 本文向大家介绍事件委托的优点?相关面试题,主要包含被问及事件委托的优点?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: 把一个元素响应事件(click、keydown......)的函数委托到另一个元素; 优点:减少内存消耗、动态绑定事件。