【问题标题】:How to customize event source for ActionEvent?如何自定义 ActionEvent 的事件源?
【发布时间】:2016-08-11 22:17:25
【问题描述】:

我正在开发一个自定义组件,但遇到了一个问题。这是组件:

public class MyComponent extends JPanel {

    private final JButton jButton;
    private final JLabel jLabel;

    public MyComponent(){
        jButton = new JButton();
        //etc..
    }

    public void addActionListener(ActionListener l){
         //The problem with this is that ActionEvent has source attribute 
         //set to jButton which is not desirable. How can I set it to this?
         jButton.addActionListener(l);
    }

    //other component-specific methods
}

问题是我试图隐藏MyComponent 的实现细节。但是以这种方式设置监听器并不好,因为调用者可能会观察到source attrubte 是jButton。如何设置为封闭的MyComponent 实例?

【问题讨论】:

  • jButton.add(l); 你不是说jButton.addActionListener(l); 吗?
  • @11thdimension 是的,已修复。谢谢。
  • 问题是我试图隐藏 MyComponent 的实现细节。 == EventHandler

标签: java swing inheritance actionlistener


【解决方案1】:

以下应该可以工作。

@SuppressWarnings("all")
public class MyComponent extends JPanel {
    private final JButton jButton = new JButton();
    private final JLabel jLabel = new JLabel();

    public void addActionListener(final ActionListener listener) {
        final MyComponent self = this;

        ActionListener newListener = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ActionEvent newEvent = new ActionEvent(e.getSource(), e.getID(), e.getActionCommand()) {
                    @Override
                    public Object getSource() {
                        return self;
                    }
                };
                listener.actionPerformed(newEvent);
            }
        };
        jButton.addActionListener(newListener);
    }
}

带有 Lambda 表达式(单数)

@SuppressWarnings("all")
public class MyComponent2 extends JPanel {
    private final JButton jButton = new JButton();
    private final JLabel jLabel = new JLabel();

    public void addActionListener(final ActionListener listener) {
        MyComponent2 self = this;

        jButton.addActionListener(e-> {
            ActionEvent newEvent = new ActionEvent(e.getSource(), e.getID(), e.getActionCommand()) {
                @Override
                public Object getSource() {
                    return self;
                }
            };
            listener.actionPerformed(newEvent);
        });
    }
}

【讨论】:

  • 我建议通过切换到监听器的lambda来避免匿名类,并通过ActionEvent的构造函数指定源而不是覆盖getSource()方法
  • Lambda 表达式是匿名类,我会尝试将代码转换为等效的 lambda。
  • 它们不是匿名类,编译后也不会生成任何额外的二进制文件。 Lambda 不仅仅是语法糖——查看字节码。
  • 更新代码以使用Lamda表达式并修复代码中的错误。 @Vince Emign 如您所见,我们在这里使用 Lambda 表达式并没有节省太多。
  • 它摆脱了样板代码,使其更易于阅读,降低了理解代码所需的认知(省略了标识符,参数类型信息是可选的,没有访问修饰符)。它隐藏了涉及对象的事实,因为这种行为的目的是将一个函数传递给另一个函数,这再次释放了认知。它还消除了从匿名类生成的多余类文件,从而减少了类加载器必须做的工作量。相信我,在计算和心理处理方面都节省了很多。
【解决方案2】:

不要让客户端传入ActionListener,而是让客户端传入一个不同的回调,创建自己的监听器,然后让你的监听器调用回调:

public class MyComponent extends JPanel {
    private final JButton jButton;

    public MyComponent(){
        jButton = new JButton();
    }

    public void addActionListener(SomeCallback callback){
        jButton.addActionListener(event -> { //create listener
            callback.execute(); //invoke callback
        });
    }
}

interface SomeCallback {
    void execute();
}

如果您想将ActionEvent 传递给无法访问ActionEvent#getSource() 的客户端,请创建一个包装器:

class ActionEventWrapper {
    private ActionEvent event;

    public MyActionEvent(ActionEvent event) {
        this.event = event;
    }

    //expose methods that aren't getSource()
    public String getActionCommand() {
        return event.getActionCommand();
    }
}

只需将此类型添加到回调的方法参数中:

interface SomeCallback {
    void execute(ActionEventWrapper event);
}

然后,您可以在任何时候触发事件时创建一个新的ActionEventWrapper

    public void addActionListener(SomeCallback callback){
        jButton.addActionListener(event -> {
            callback.execute(new ActionEventWrapper(event));
        });
    }

如果你真的想调整组件监听事件的来源,只需创建一个新的ActionEvent,通过构造函数指定你想要的任何来源:

public void addActionListener(ActionListener listener) {
    jButton.addActionListener(event -> {
        listener.actionPerformed(new ActionEvent(..., event.getID(), event.getActionCommand()));
    });
}

... 用于指定要充当源的组件。

【讨论】:

  • 实际上这是我的解决方案。但是最初的问题是什么......如何自定义ActionEventsource
  • @user3663882 你不能。 ActionEvent 是在“幕后”创建的,一旦触发动作,源就会传递给事件。虽然我确实添加了一个选项来传递回调一个不包含getSource()ActionEvent。让我知道这是否有帮助!
  • @user3663882 如果您还有其他疑问,或者此设计不符合您的需求,请告诉我(请解释原因,以便我修正答案)跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多