12.2.4 RadioButton2D类
如果用户喜欢Button2D类,那么也肯定会喜欢RadioButton2D类。RadioButton2D模仿CheckBox类,它允许用户区别按钮的“on”和“off”状态。它还允许把几个单选按钮关联到一个组中,这样确保在给定的事件内这个组中只有一个成员处于选中状态。
令人惊讶的是,RadioButton2D类比Button2D要简单一些。首先,它只有两个状态,on和off。我们也可以把它看作选中和没选中,或者其他喜欢的词汇。此外,RadioButton2D在被按下时不需要激发事件。很多应用程序主动地检索单选按钮的状态而不是等待它激发事件。
RadioButton2D类还实现了MouseListener接口,但是没有实现MouseMotionListener接口,这是因为鼠标移动事件已不再适用。
下面是RadioButton2D类,其中的文字注释使得它易于理解。
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;

public class RadioButton2D extends Component2D implements MouseListener...{
//和按钮一起显示的标签
protected Label2D label;
//这个按钮所从属的组
protected RadioButtonGroup rbGroup;
//跟踪按钮状态的常量
public static final int BUTTON_OFF=0;
public static final int BUTTON_ON=1;
//用给定的标签,图像,按钮组和位置创建一个RadioButton2D
public RadioButton2D(Label2D lbl,ImageGroup grp,RadioButtonGroup rbg,

Vector2D p)...{
super();
label=lbl;
group=grp;
rbGroup=rbg;

if(rbGroup!=null)...{
rbGroup.add(this);
}
setPos(p);
updateBounds();
update();
setSelected(false);
}
//用给定的图像,按钮组和位置创建一个新的RadioButton2D

public RadioButton2D(ImageGroup grp,RadioButtonGroup rbg,Vector2D p)...{
this(null,grp,rbg,p);
}
//用给定的标签,图像和按钮组创建一个新的RadioButton2D

public RadioButton2D(Label2D lbl,ImageGroup grp,RadioButtonGroup rbg)...{
this(lbl,grp,rbg,Vector2D.ZERO_VECTOR);
}
//用给定的图像组创建一个新的RadioButton2D

public RadioButton2D(ImageGroup grp)...{
this(null,grp,null,Vector2D.ZERO_VECTOR);
}
//返回按钮是否被选中

public boolean isSelected()...{
return (state==BUTTON_ON);
}
//设置按钮的选中状态

public void setSelected(boolean selected)...{
state=(selected)?BUTTON_ON:BUTTON_OFF;
}
//如果有,返回按钮的文本描述

public String getText()...{

if(label!=null)...{
return label.getText();
}
return "";
}
//设置按钮和它的标签的可用状态

public void setEnabled(boolean e)...{
super.setEnabled(e);

if(label!=null)...{
label.setEnabled(e);
}
}
//变换按钮的状态

public void mousePressed(MouseEvent e)...{
if(!isEnabled()||!isVisible())return;
//如果没有按钮组则表明按钮独立行动

if(rbGroup==null)...{

if(bounds.contains(e.getPoint()))...{
setSelected(!isSelected());
}

}else...{//否则,更新整个按钮组

if(bounds.contains(e.getPoint()))...{
setSelected(true);
rbGroup.updateGroup(this);
}
}
}

public void mouseEntered(MouseEvent e)...{
}

public void mouseExited(MouseEvent e)...{
}

public void mouseClicked(MouseEvent e)...{
}

public void mouseReleased(MouseEvent e)...{
}
//用当前的变换绘制按钮

public void paint(Graphics2D g2d)...{
//只在组件可见时绘制

if(isVisible())...{
g2d.drawImage(((ButtonImageGroup)group).getFrame(state),
xform,AnimationStrip.observer);
//如果标签有效则绘制标签

if(label!=null)...{
label.paint(g2d);
}
}
}
//在给定的位置绘制按钮

public void paint(Graphics2D g2d,double dx,double dy)...{
//只绘制可见组件

if(isVisible())...{
g2d.drawImage(((ButtonImageGroup)group).getFrame(state),
AffineTransform.getTranslateInstance(pos.getX()+dx,
pos.getY()+dy),AnimationStrip.observer);
//如果标签有效则绘制标签

if(label!=null)...{
label.paint(g2d,dx,dx);
}
}
}
//返回描述这个按钮的文本

public String toString()...{

if(label!=null)...{
return super.toString()+" "+label.toString();
}
return super.toString();
}
}//RadioButton2D

原来看起来好像RadioButton2D类应该继承Button2D类,毕竟单选按钮可以被认为是一种按钮。然而,出于前面提到的考虑,自定义单选按钮类与标准按钮类差异太大,难以直接派生。RadioButton2D将承接太多不必要的包袱,这是每一个人都不希望看到的。此外,在运行时使用new运算符把Button2D对象转变为RadioButton2D对象也没有什么好处,它们完全是功能不同的组件。
RadioButton2D需要的一个特性是外部分组,这样可以实现多选一。自定义的RadioButtonGroup类恰好实现了这个特性,它包含一个可以增长(也可以收缩)的Vector对象来容纳单选按钮。通过这种方式,就无须在事前知道组中会有多少个单选按钮。这个类还包含一些方法,这些方法包括把单选按钮添加到组中,找到当前被选中的单选按钮,设置激活和可视状态属性,更新哪个按钮被选中以及绘制组中的单选按钮。
import java.awt.*;
import java.util.*;
//维护一组按钮,这样在任意时刻只有一个被选中

public class RadioButtonGroup...{
//用来装单选按钮的动态可增长Vector
protected Vector buttons;
//遍历上述Vector的枚举器
protected Enumeration e;
//创建一个新的RadioButtonGroup对象

public RadioButtonGroup()...{
buttons=new Vector();
}
//将传入的单选按钮添加到Vector中去

public void add(RadioButton2D rb)...{
buttons.add(rb);
}
//得到当前所选中的单选按钮
//如果没有按钮被选中,则返回null

public RadioButton2D getSelection()...{

for(e=buttons.elements();e.hasMoreElements();)...{
RadioButton2D rb=(RadioButton2D)e.nextElement();

if(rb.isSelected())...{
return rb;
}
}
return null;
}
//整组按钮的可用/不可用状态

public void setEnabled(boolean b)...{

for(e=buttons.elements();e.hasMoreElements();)...{
((RadioButton2D)e.nextElement()).setEnabled(b);
}
}
//设置整个按钮组的可见性

public void setVisible(boolean v)...{

for(e=buttons.elements();e.hasMoreElements();)...{
((RadioButton2D)e.nextElement()).setVisible(v);
}
}
//更新按钮组,让除了传入的按钮之外的按钮设置为未选中状态

public void updateGroup(RadioButton2D rb)...{

for(e=buttons.elements();e.hasMoreElements();)...{
Object o=e.nextElement();

if(rb!=o)...{
((RadioButton2D)o).setSelected(false);
}
}
}
//绘制组中的每一个按钮

public void paint(Graphics2D g2d)...{

for(e=buttons.elements();e.hasMoreElements();)...{
((RadioButton2D)e.nextElement()).paint(g2d);
}
}
//在给定的位置绘制组中的每一个按钮

public void paint(Graphics2D g2d,double dx,double dy)...{

for(e=buttons.elements();e.hasMoreElements();)...{
((RadioButton2D)e.nextElement()).paint(g2d,dx,dy);
}
}
}//RadioButtonGroup
这个程序简洁而高效,那么怎么用一种比较好的方式在一个代码例子中演示单个和成组单选按钮呢?下面的RadioButton2DTest applet,包含了一个单选按钮组,它允许用户从几种超能力中作出选择,它还包含一个独立的单选按钮,它可以使包含超能力选择的单选按钮组激活或者失效。这个演示比较有趣,建议读者试一下。
import java.applet.*;
import java.awt.*;

public class RadioButton2DTest extends Applet