代码之家  ›  专栏  ›  技术社区  ›  ivan_ivanovich_ivanoff

Swing:如何实现从子组件到父容器的所有事件的转发?

  •  2
  • ivan_ivanovich_ivanoff  · 技术社区  · 16 年前

    我在找一个简单的方法 所有接收到的前摆组件 事件到其父容器 (甚至是所有的父母都是如此)。

    编辑 :
    我在哪里需要这个?我有一个图表编辑器。部件必须向前按键和 鼠标单击(当用户单击子元素时将其自身设置为“活动” 对那个部件)。

    首先,让我介绍一下我现有的解决方案。这是一种变通办法。

    public interface IUiAction {
     void perform(Component c);
    }
    
    public static void performRecursiveUiAction(Container parent, IUiAction action) {
     if (parent == null) {
      return;
     }
    
     for (Component c : parent.getComponents()) {
      if (c != null) {
       action.perform(c);
      }
     }
    
     for (Component c : parent.getComponents()) {
      if (c instanceof Container) {
       performRecursiveUiAction((Container) c, action);
      }
     }
    }
    
    /**
    * 1) Add listener to container and all existing components (recursively).
    * 2) By adding a ContainerListener to container, ensure that all further added
    * components will also get the desired listener.
    *
    * Useful example: Ensure that every component in the whole component
    * tree will react on mouse click.
    */
    public static void addPermanentListenerRecursively(Container container,
      final IUiAction adder) {
    
     final ContainerListener addingListener = new ContainerAdapter() {
      @Override
      public void componentAdded(ContainerEvent e) {
       adder.perform(e.getChild());
      }
     };
    
     // step 1)
     performRecursiveUiAction(container, adder);
    
     // step 2)
     performRecursiveUiAction(container, new IUiAction() {
      @Override
      public void perform(Component c) {
       if (c instanceof Container) {
        ((Container) c).addContainerListener(addingListener);
       }
      }
     });
    }
    

    用途:

    addPermanentListenerRecursively(someContainer,
      new IUiAction(
        @Override
        public void perform(Component c){
          c.addMouseListener(somePermanentMouseListener);
        }
      )
    );
    

    通过检查代码,你会说这是一个好的概念吗?
    我当前的概念有一个问题:它只转发事件,手工指定了一个侦听器。

    你能推荐一个更好的吗?

    3 回复  |  直到 13 年前
        1
  •  2
  •   akf    16 年前

    如果视图中的每个组件处理相同的鼠标事件,这看起来会有点棘手。也就是说,如果用户拖动 项目1 , 项目2 也会处理这些事件吗?如果我理解正确的话,你应该 this this.parent this.parent.parent 在上处理鼠标操作 . 在这种情况下,递归是向上的,而不是向下的。

    你可以创建一个 Interface 这样地:

    public interface MyInterface() {
       public void performSpecialAction(Event event);
    }
    

    然后让容器实现这个接口。然后,您的组件需要将此调用合并到相应的事件处理中:

    public static void performRecursiveUiAction(Component comp, Event event) {
      if (comp.getParent() == null) {
         return;
      }
      if (comp.getParent() instanceof MyInteface) {
         ((MyInterface)comp.getParent()).performSpecialAction(event);
      }
      ThisUtility.performRecursiveUiAction(comp.getParent(), event);
    

    }

        2
  •  3
  •   akarnokd    16 年前

    根据您的场景,我有一个关于键盘方面的建议:

    您可以使用Swing的击键功能:

    JRootPane rp = getRootPane();
    
    KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0, false);
    rp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, "F2");
    rp.getActionMap().put("F2", new AbstractAction() { 
    public void actionPerformed(ActionEvent e) { onF2Action(); } });
    

    通过这种方式,您可以为快捷方式注册“全局”事件处理程序 .

    尽管它在您的案例中可能有一些限制。

    对于鼠标事件的情况,我将创建一个递归函数,它添加一个 MouseAdapter 每个目标组件的实例。例如:

    void addToAll(Container c, MouseAdapter a) {
        for (Component p : c.getComponents()) {
            if (p instanceof InterrestingComponent) {
                p.addMouseListener(a);
                p.addMouseMotionListener(a);
                p.addMouseWheelListener(a);
            } else
            if (p instanceof Container) {
                addToAll((Container)p, a);
            }
        }
    }
    

    只是为了抓住这个概念。对于不同的组件,您可能需要不同的或多个接收器。

    编辑: 对不起,我不小心说的是WindowAdapter而不是MouseAdapter。

        3
  •  1
  •   MoZo1    13 年前
    • 要么从组件中获取事件,在本例中,您注册需要捕获事件的侦听器。pro是指您可以选择在何处捕获事件,并且您可以选择按来源选择性地接收事件。缺点是你必须注册尽可能多的听众,尽可能多的资源…
    • 或者使用玻璃面板,在它传播到组件之前捕获所有内容。在这种情况下,您可以捕获它们,如果您想将它们绑定到玻璃窗格下的组件,则必须编写自定义处理代码。(假设您保持玻璃窗格为空…)下面是一个示例,它在事件到达组件之前捕获事件,甚至将其转发到选定的组件: http://download.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane
    • 中间部分是使用分层窗格创建透明组件(类似于玻璃窗格的东西),在您需要的任何内容之上。

    也可以使用()事件…您的主要问题是权衡,您要么在Swing句柄之前捕获事件(并从头开始编写自定义处理),要么在Swing句柄之后捕获事件(并且可能会受到一些处理的干扰),但您不能通过子类化任何东西来修改处理代码本身(或者您可以这样做吗?从来没有见过……)。