WEB开发网
开发学院WEB开发Jsp Java技巧: 推动JButtonGroup 阅读

Java技巧: 推动JButtonGroup

 2008-01-05 09:21:45 来源:WEB开发网   
核心提示:摘要 Swing的 ButtonGroup 类答应单选按钮分组以保证单一选择;但是,这个实现引起了许多问题标记,Java技巧: 推动JButtonGroup,你不能检索到组中当前选择的按钮的引用,并且类答应你选择或者取消选择通过引用访问的任何按钮, 关于作者 Daniel Tofan是纽约Stony Brook州立大学

  摘要

Swing的 ButtonGroup 类答应单选按钮分组以保证单一选择;但是,这个实现引起了许多问题标记。你不能检索到组中当前选择的按钮的引用,并且类答应你选择或者取消选择通过引用访问的任何按钮,不是组中成员也可。本技巧描述了当提供便利的、使得JButtonGroup更加易于使用的方法时JButtonGroup 子类ButtonGroup如何提供更为可靠的实现。

Swing有许多有用的类,这些类可简化绘图用户界面(GUI)的开发。 但是,其中有一些类实现起来不那么轻易,如ButtonGroup。本文解释了为什么ButtonGroup 难于设计并提供了一个代替的类,JButtonGroup,它继续了ButtonGroup并解决了ButtonGroup的某些问题。

注重: 你可从Resources处下载本文的源代码。

ButtonGroup 突破口

在开发Swing GUI 经常会出现这样的情况:你建立了一个表格来收集关于输入到数据库或者保存到文件中的数据项。这个表格可能包括文本框、单选按钮和其他窗口小部件。你使用ButtonGroup 类将所有需要单一选择的单选按钮分组。当表格设计预备好了的时候,你开始填写表格数据。你碰到一组单选按钮,你需要知道组中哪个按钮被选了这样你就能储存恰当的信息到数据库或者文件中。你现在卡住了。为什么? ButtonGroup 类不提供到组中当前所选按钮的引用。

ButtonGroup有一个getSelection()方法,它返回所选按钮的模型(作为ButtonModel 类型),但不是按钮本身。现在,假如你能够从模型中得到按钮引用那也好,但是你得不到。ButtonModel 接口和它的实现类不答应你从它的模型中检索按钮引用。那你怎么办?你仔细看看ButtonGroup 文档你会看到getActionCommand()方法。你想想假如你使用按钮旁边显示文本的String 来例示JRadioButton 的话,你调用按钮上的getActionCommand(),构造器中的文本就会返回。你可能会以为你仍然使用代码工作,因为虽然你没有按钮引用,至少你有了它的文本而且仍然知道所选的按钮。

好,太棒了!可希奇的是,你的代码在运行过程出现NullPointerException。为什么?因为ButtonModel中的getActionCommand() 返回null。假如你赌(像我一样) getActionCommand()在按钮上的调用或者在模型上的调用也出现了同样的异常(许多其他的方法都是这样,如 isSelected()、isEnabled()、或者getMnemonic()),那你就错了。假如你确实没有调用按钮上的setActionCommand(),你没有在它的模型中设置作用指令的话, 获得器方法就会返回对于模型的null 。但是,调用按钮上的获得器方法,它确实会返回按钮文本。下面就是AbstractButton 中的 getActionCommand() ,继续于Swing 中的所有按钮类: public String getActionCommand() {
String ac = getModel().getActionCommand();
if(ac == null) {
ac = getText();
}
return ac;
}



设置和获得行为指令之间的矛盾是无法接受的。假如当行为指令为空时,AbstractButton 中的setText()设置模型的行为指令到按钮文本上,你就能避免这种矛盾。究竟,除非setActionCommand()使用某些String参数(非空)来调用,否则按钮文本总是被按钮本身当作行为指令。为什么模型表现如此不同呢?

当你的代码需要到ButtonGroup 中的当前所选按钮的引用,你得遵循下列步骤,这些步骤中没有一个调用了getSelection():

?调用ButtonGroup 上的getElements(),它将返回一个Enumeration

?通过Enumeration迭代,以便得到每个按钮的引用

?调用每个按钮上的 isSelected(),判定出它是否被选

?返回引用到返回true 的按钮上

?或者,假如你需要行为指令,调用按钮上的getActionCommand()

假如这些步骤看起来过于繁杂的话,请往下读。我认为 ButtonGroup的实现根本上是错误的。它保存的是到所选按钮模型的引用,实际上它应该保存到所选按钮的引用。而且, getSelection()检索所选按钮的方法,你会认为相应的设置器方法就是setSelection(),可实际上它不是,而是setSelected()。现在,setSelected()有一个大问题。它的参数是ButtonModel和布尔值。假如你调用ButtonGroup 上的setSelected(),并传递按钮的非该组成员的模型和参数true , 那么该按钮被选,组中其他按钮则为非被选。换句话说, ButtonGroup 有权决定传递到它的方法处的按钮选择还是不选择,即使该按钮与该组毫无关系。这种行为是存在的,因为ButtonGroup 中的setSelected()不检查作为参数收到的 ButtonModel引用是否代表组中的按钮。而且由于该方法坚持单一选择,它实际上不选自己的按钮而选择与该组无关的按钮。

这种约定在ButtonGroup文档中表现得更为有趣:

我们无法为了清除按钮组而程序化地关闭按钮。为了表现出“非被选”,添加一个可见的单选按钮到组中,然后程序化地选择该按钮来关闭所有显示的单选按钮。例如:可安装带有卷标´´none´´的普通按钮来选择可见的单选按钮。

很好,但是这不现实:你可以使用任何按钮,放在你的应用程序中的任何位置,可见或不可见,设置也可以是无效的。是的,你甚至可以使用按钮组来选择一个组外的无效按钮,并且它仍然还可以取消对它所有按钮的选择。要得到组中所有按钮的引用,你得调用这个机械的getElements()。"elements" 能对ButtonGroup做些什么,大家都知道。getElements(),的命名可能就是受Enumeration类的方法 (hasMoreElements()和nextElement())所启发,但是getElements()无疑应该命名为 getButtons()。因为按钮组分组的是按钮,而不是元素。

你可以在本文最后一页的javaWorld Talkback发表你的评论,看看你的同行们会如何回答你。Solution: JButtonGroup

由于以上原因,我想要实现一个新的类,这个类可以解决ButtonGroup 中的错误并且提供一些功能和便利给用户。我不得不决定创建一个新类还是由ButtonGroup衍生。所有上述的讨论都建议我创建一个新类而不是创建ButtonGroup的子类。但是, ButtonModel接口需要一个能够接受ButtonGroup参数的方法setGroup()。除非我也预备重新实现按钮模型,否则我只能选择创建ButtonGroup 子类并用它覆盖ButtonGroup的大部分方法。说到 ButtonModel接口,注重它不包括方法 getGroup()。

我没有提到的另一个方案就是ButtonGroup 内部将到它的按钮的引用保存在Vector中。当他使用ArrayList时,因为这个类本身为非线程安全,同时Swing是单线程的。因此,它没有必要得到同步的Vector的系统开销。但是,受保护的变量buttons公告为Vector 类型,而不是你希望的良好编程的类型List 。因此,我不能把这个变量当作ArrayList 来实现;并且因为我想调用super.add()和super.remove(),我不能隐藏超类变量。所以我放弃了这个方案。

我假设类JButtonGroup与大部分的Swing类的名称保持一致。该类可遍历ButtonGroup 中的大部分方法并能提供额外的便利的方法。它保存到当前所选按钮的引用,你可以使用一个简单的到getSelected()的调用来检索它。由于ButtonGroup的实现能力很差,我可以将我的方法命名为getSelected(),因为getSelection()方法返回的是按钮模型。

下面就是JButtonGroup的方法。

首先,我得对add()方法作两个修改:假如需要添加的按钮在组中,方法返回。因此,你只能添加按钮到组中一次。有了ButtonGroup,你可以创建一个JRadioButton 并将它添加到组中10次。调用getButtonCount() 则会返回10。这一般不会这样用,所以我不答应复制的引用。这样,假如所添加的按钮前面选择过,那它就会变成所选按钮(这种行为在ButtonGroup 中是默认的,这是合理的。所以我不想修改它)。变量selectedButton 是到组中当前所选按钮的引用:

public void add(AbstractButton button)
{
if (button == null buttons.contains(button)) return;
super.add(button);
if (getSelection() == button.getModel()) selectedButton = button;
}



超载的add()方法添加了整个排列的按钮到组中。当你想储存按钮引用到排列中以便于块处理(如设置边界,添加行为听取器等等),这是很有用的:

public void add(AbstractButton[] buttons)
{
if (buttons == null) return;
for (int i=0; i<buttons.length; i++)
{
add(buttons[i]);
}
}



下面的两个方法删除了组中的一个按钮或者一个排列的按钮:

public void remove(AbstractButton button)
{
if (button != null)
{
if (selectedButton == button) selectedButton = null;
super.remove(button);
}
}

public void remove(AbstractButton[] buttons)
{
if (buttons == null) return;
for (int i=0; i<buttons.length; i++)
{
remove(buttons[i]);
}
}



今后,第一个setSelected()方法答应你通过按钮引用而不是通过按钮模型来设置按钮的选择状态。第二个方法顶替了ButtonGroup 中相应的setSelected(),以保证组只能选择或者取消选择组中的按钮:

public void setSelected(AbstractButton button, boolean selected)
{
if (button != null && buttons.contains(button))
{
setSelected(button.getModel(), selected);
if (getSelection() == button.getModel()) selectedButton = button;
}
}

public void setSelected(ButtonModel model, boolean selected)
{
AbstractButton button = getButton(model);
if (buttons.contains(button)) super.setSelected(model, selected);
}



getButton()方法检索了到模型给出的按钮的引用。setSelected()使用这个方法来检索 所选的要求给出它的模型的按钮。假如传递到方法的模型属于组外的按钮,则getButton()返回null 。这个方法应该存在于ButtonModel 实现中,不幸的是它没有:

public AbstractButton getButton(ButtonModel model)
{
Iterator it = buttons.iterator();
while (it.hasNext())
{
AbstractButton ab = (AbstractButton)it.next();
if (ab.getModel() == model) return ab;
}
return null;
}



getSelected()和 isSelected()是JButtonGroup类中最简单的,也是最有用的方法。getSelected()返回到所选按钮的引用并且isSelected() 超载ButtonGroup 中的同名方法来得到按钮引用:

public AbstractButton getSelected()
{
return selectedButton;
}

public boolean isSelected(AbstractButton button)
{
return button == selectedButton;
}



该方法检查按钮是否为组中成员:

public boolean contains(AbstractButton button)
{
return buttons.contains(button);
}



你可能希望在ButtonGroup 类中有一个名为getButtons()的方法。它返回一个固定的表单,这个表单包含到组中按钮的引用。固定的表单可以防止不经过按钮组的方法就添加或者删除按钮。ButtonGroup 中的getElements()不但名字缺乏创见,而且它返回的Enumeration是你不应该使用的已废弃的类。收集框架(Collections Framework )提供了你需要避免列举的每件事物。下面的程序显示了getButtons() 如何返回一个固定不变的表单:

public List getButtons()
{
return Collections.unmodifiableList(buttons);
}



改进ButtonGroup

JButtonGroup类在保留所有超类的功能的同时,为Swing ButtonGroup类提供了一个更好更便利的备选类。

关于作者

Daniel Tofan是纽约Stony Brook州立大学化学部的一个博士后的助手。他的工作包括使用化学中的应用程序开发课程治理系统的核心部分。他也是Sun 认证的Java 2平台的程序员同时还是一名化学博士。

Tags:Java 技巧 推动

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接