Swing中的操作监听器和事件源

4

好的,如果我将ActionListener 添加到GUI元素中,并且这是我唯一使用该ActionListener 的元素,我使用以下哪些行(a,b)来获取复选框选择状态是否重要?

final JCheckBox checkbox = (JCheckBox)this.buildResult.get("cbDebugTick");
checkbox.addActionListener(new ActionListener() {
    @Override public void actionPerformed(ActionEvent event){               
            boolean bChecked =
            // (a) checkbox.isSelected();
            // (b) ((JCheckBox)event.getSource()).isSelected();
            model.setPrintDebugOn(bChecked);
        }
});

我认为如果我将ActionListener对象添加到多个GUI元素中,那么使用(b)是有道理的。

在(b)中,盲目地将event.getSource()强制转换为JCheckBox是否可以,因为我是添加动作监听器的人,还是应该编写防御性代码并进行instanceof检查?

注意:这个问题是在事件监听器的一般上下文中;kdgregory在下面提出了一些关于复选框的好观点,我没有考虑到。

4个回答

3
我两个都不会做。
如果点击复选框将启动某些操作,我会附加一个ItemListener,然后只需查看ItemEvent中的选择状态。
然而,复选框通常不会调用操作,它们管理状态。因此,在响应任何触发操作的情况下,检查所有复选框是更好的方法。

编辑:关于OP提出的更大问题的一些评论。

首先,重要的是要意识到,Swing的许多部分代表了实现方便而不是一致的行为模型。 JCheckBoxJButton 除了它们的空间内点击有意义之外,没有任何共同点。然而,它们都继承自 AbstractButton,该类提供了实现细节,例如按钮的标签。它还假定按钮被“按下”,并且按下按钮会启动一些有意义的行为(操作)。然而,在JCheckbox的情况下,按钮按下并不重要,状态变化是重要的。该状态变化被信号传递给ItemListener - 即使状态变化对其他按钮类型没有意义,该接口也在AbstractButton上定义(JavaDoc甚至说“复选框”)。

Swing做对的一件事情(如果难以使用)是将Action与启动该操作的控件分离的想法。 Action对象可以从多个控件调用:菜单项,对话框上的推按钮,按键等等。从设计角度来看更重要的是,它使您远离试图弄清需要发生什么的通用“监听器”的思想。例如,我见过应用程序其中一个监听器从整个菜单系统接收输入,然后运行一个大的if/else链来确定哪个菜单项被按下。使用Actions意味着你有更多的类,但从长远来看,这会给你一个更可维护的应用程序。
最后,从可用性的角度来看,保持状态的控件(如JCheckbox和JTextArea)和启动操作的控件(如JButton和JMenuItem)之间存在差异。我曾经看到过一个(Web)应用程序,点击单选按钮会带您到另一页。那不好。即使您计划在内部使用监听器来更新某些模型的状态,您也应该问自己为什么GUI元素的集合本身没有为您提供模型。

是的,但可能会有其他人通过谷歌找到这个问题,并更愿意适当地使用API,而不是担心哪种方法更好。 - kdgregory
这是公平的,我会在问题中注明。另外,在结尾处给予另一个评论+1:在这种情况下,我使用监听器将复选框状态传播到一个不知道复选框的模型对象上,我想我应该使用javax.swing.JToggleButton.ToggleButtonModel而不是事件监听器。 - Jason S
1
我也应该用JButton或其他JComponent的术语来提问我的问题;这个问题在我处理事件监听器时曾经出现过,我想知道适当的最佳实践。 - Jason S

1

对于监听器是独占的情况(例如匿名监听器),我使用(a)。

如果监听器将被重复使用(例如,this 是 ActionListener 的实例),我会将其写成:

@Override
public void actionPerformed(ActionEvent event) {
    Object src = event.getSource();
    if (src == checkbox) {
        boolean bChecked = checkbox.isSelected();
        // ...
    }
}

如果您有多个复选框,并且它们以相同的方式进行处理,则使用instanceof是有意义的。

0

为了严谨起见,您确实应该进行 instanceof 检查,但这并不是非常重要的。我认为这两行代码都可以接受和可行,尽管 (b) 更好一些。

通常在动作监听器中所做的是调用另一个针对您的复选框定制的方法。因此,它看起来像这样:

 @Override public void actionPerformed(ActionEvent event) {                                  
    //your treatment would be in this method, where it would be acceptable to use (a)                  
    onCheckBoxActionPerformed(event)
}

你能详细说明一下吗?如果在匿名内部类中可以访问相同的方法/字段,为什么要将其转发到外部类? - Jason S
这样你可以将不同的组件添加到同一动作监听器中,并以不同的方式或多态地对待它们,使代码“更好”。 - David Menard
1
我不同意David的说法。像这样转发调用违背了解耦合的原则(这也是Swing中采用监听器方法进行事件通知的原因)。理想情况是您可以完全分离操作行为,并在包含控件的视图外部使用操作监听器 - 在这种情况下,选项(b)是首选技术。话虽如此,您真正应该做的是创建一个Action并将控件附加到它上面。 - Kevin Day
FYI,这是大多数IDE在使用图形GUI创建器时生成自动化代码的方式,例如:NetBeans。 - David Menard

0

我会以最佳实践选项的方式进行防御性编程。但是,如果只有您自己使用代码,则没有理由不能选择另一种方式。然而,想象一下,如果您在将来的某个时间点回到它并更改了某些内容,并发现您编写了可以直接重用的良好代码,那么您会感到多么高兴...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接