菜单(Menu)



每个组件都能够持有一个菜单,包括 JApplet, JFrame, JDialog以及它们的子类。它们的
setJMenuBar( )方法接受一个JMenuBar对象(每个组件只能持有一个JMenuBar对象)
作为参数。你先把JMenu对象添加到JmenuBar中,然后把JmenuItem添加到Jmenu中。
每个JmenuItem都能有一个相关联的ActionListener,用来捕获菜单项被选中时触发的事
件。


与使用“资源文件”的系统不同,在 Java 和 Swing 中你必须在源代码中构造所有的菜单。
下面是个非常简单的菜单例子:


//: c14:SimpleMenus.java
// <applet code=SimpleMenus width=200 height=75></applet>
import javax.swing.*;
import java.awt.event.*; 
import java.awt.*; 
import com.bruceeckel.swing.*; 


public class SimpleMenus extends JApplet { 
private JTextField t = new JTextField(15);
private ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) { 
      t.setText(((JMenuItem)e.getSource()).getText());
    }
  };
private JMenu[] menus = { 
new JMenu("Winken"), new JMenu("Blinken"),
new JMenu("Nod")
  };
private JMenuItem[] items = { 
new JMenuItem("Fee"), new JMenuItem("Fi"),
new JMenuItem("Fo"), new JMenuItem("Zip"), 
new JMenuItem("Zap"), new JMenuItem("Zot"), 
new JMenuItem("Olly"), new JMenuItem("Oxen"), 
 






new JMenuItem("Free")
  };
public void init() { 
for(int i = 0; i < items.length; i++) {
      items[i].addActionListener(al); 
      menus[i % 3].add(items[i]);
    }
    JMenuBar mb = new JMenuBar();
for(int i = 0; i < menus.length; i++) 
      mb.add(menus[i]); 
    setJMenuBar(mb); 
    Container cp = getContentPane(); 
    cp.setLayout(new FlowLayout()); 
    cp.add(t);
  }
public static void main(String[] args) { 
    Console.run(new SimpleMenus(), 200, 75);
  }
} ///:~


程序中通过取模运算“i%3”把菜单项分配给三个Jmenu。每个JMenuItem必须有一个相
关联的ActionListener;这里使用了同一个ActionListener,不过通常你要为每个
JmenuItem都单独准备一个ActionListener。


JMenuItem 从 AbstractButton 继承而来,所以它具有类似按钮的行为。它提供了一个可
以单独放置在下拉菜单上的条目。还有三种类型继承自 JMenuItem:JMenu 用来持有其
它的 JMenuItem(这样才能实现层叠式菜单);JCheckBoxMenuItem 提供了一个检查
标记,用来表明菜单项是否被选中;JRadioButtonMenuItem 包含了一个单选按钮。


下面是一个更复杂的例子,这里仍然是冰激凌口味菜单。这个例子还演示了层叠式菜单、
菜单快捷键、JCheckBoxMenuItem,以及动态改变菜单的方法:


//: c14:Menus.java
// Submenus, checkbox menu items, swapping menus,
// mnemonics (shortcuts) and action commands.
// <applet code=Menus width=300 height=100></applet>
import javax.swing.*;
import java.awt.*; 
import java.awt.event.*; 
import com.bruceeckel.swing.*; 


public class Menus extends JApplet {
private String[] flavors = { 
"Chocolate", "Strawberry", "Vanilla Fudge Swirl",
"Mint Chip", "Mocha Almond Fudge", "Rum Raisin",
 






"Praline Cream", "Mud Pie"
  };
private JTextField t = new JTextField("No flavor", 30); 
private JMenuBar mb1 = new JMenuBar(); 
private JMenu 
    f = new JMenu("File"),
    m = new JMenu("Flavors"), 
    s = new JMenu("Safety");
// Alternative approach:
private JCheckBoxMenuItem[] safety = { 
new JCheckBoxMenuItem("Guard"),
new JCheckBoxMenuItem("Hide")
  };
private JMenuItem[] file = { new JMenuItem("Open") };
// A second menu bar to swap to:
private JMenuBar mb2 = new JMenuBar(); 
private JMenu fooBar = new JMenu("fooBar"); 
private JMenuItem[] other = { 
// Adding a menu shortcut (mnemonic) is very
// simple, but only JMenuItems can have them
// in their constructors:
new JMenuItem("Foo", KeyEvent.VK_F), 
new JMenuItem("Bar", KeyEvent.VK_A), 
// No shortcut:
new JMenuItem("Baz"), 
  };
private JButton b = new JButton("Swap Menus");
class BL implements ActionListener { 
public void actionPerformed(ActionEvent e) { 
      JMenuBar m = getJMenuBar();
      setJMenuBar(m == mb1 ? mb2 : mb1); 
      validate(); // Refresh the frame
    }
  }
class ML implements ActionListener { 
public void actionPerformed(ActionEvent e) { 
      JMenuItem target = (JMenuItem)e.getSource(); 
      String actionCommand = target.getActionCommand();
if(actionCommand.equals("Open")) { 
        String s = t.getText(); 
boolean chosen = false;
for(int i = 0; i < flavors.length; i++) 
if(s.equals(flavors[i])) chosen = true;
if(!chosen) 
 






          t.setText("Choose a flavor first!");
else
          t.setText("Opening " + s + ". Mmm, mm!"); 
      }
    }
  }
class FL implements ActionListener { 
public void actionPerformed(ActionEvent e) { 
      JMenuItem target = (JMenuItem)e.getSource(); 
      t.setText(target.getText()); 
    }
  }
// Alternatively, you can create a different
// class for each different MenuItem. Then you
// Don't have to figure out which one it is:
class FooL implements ActionListener { 
public void actionPerformed(ActionEvent e) { 
      t.setText("Foo selected"); 
    }
  }
class BarL implements ActionListener { 
public void actionPerformed(ActionEvent e) { 
      t.setText("Bar selected"); 
    }
  }
class BazL implements ActionListener { 
public void actionPerformed(ActionEvent e) { 
      t.setText("Baz selected"); 
    }
  }
class CMIL implements ItemListener { 
public void itemStateChanged(ItemEvent e) { 
      JCheckBoxMenuItem target =
        (JCheckBoxMenuItem)e.getSource(); 
      String actionCommand = target.getActionCommand();
if(actionCommand.equals("Guard"))
        t.setText("Guard the Ice Cream! " + 
"Guarding is " + target.getState()); 
else if(actionCommand.equals("Hide")) 
        t.setText("Hide the Ice Cream! " + 
"Is it hidden? " + target.getState()); 
    }
  }
public void init() { 
 






    ML ml = new ML(); 
    CMIL cmil = new CMIL(); 
    safety[0].setActionCommand("Guard"); 
    safety[0].setMnemonic(KeyEvent.VK_G); 
    safety[0].addItemListener(cmil); 
    safety[1].setActionCommand("Hide");
    safety[1].setMnemonic(KeyEvent.VK_H); 
    safety[1].addItemListener(cmil); 
    other[0].addActionListener(new FooL());
    other[1].addActionListener(new BarL());
    other[2].addActionListener(new BazL());
    FL fl = new FL(); 
for(int i = 0; i < flavors.length; i++) { 
      JMenuItem mi = new JMenuItem(flavors[i]); 
      mi.addActionListener(fl); 
      m.add(mi);
// Add separators at intervals:
if((i + 1) % 3 == 0) 
        m.addSeparator(); 
    }
for(int i = 0; i < safety.length; i++) 
      s.add(safety[i]); 
    s.setMnemonic(KeyEvent.VK_A);
    f.add(s);
    f.setMnemonic(KeyEvent.VK_F);
for(int i = 0; i < file.length; i++) { 
      file[i].addActionListener(fl); 
      f.add(file[i]);
    }
    mb1.add(f);
    mb1.add(m);
    setJMenuBar(mb1);
    t.setEditable(false); 
    Container cp = getContentPane(); 
    cp.add(t, BorderLayout.CENTER); 
// Set up the system for swapping menus:
    b.addActionListener(new BL()); 
    b.setMnemonic(KeyEvent.VK_S);
    cp.add(b, BorderLayout.NORTH); 
for(int i = 0; i < other.length; i++) 
      fooBar.add(other[i]); 
    fooBar.setMnemonic(KeyEvent.VK_B); 
    mb2.add(fooBar); 
  }
 






public static void main(String[] args) { 
    Console.run(new Menus(), 300, 100); 
  }
} ///:~


在这个程序中,我把菜单项放到了几个数组中,然后通过遍历这些数组,并为每个
JmenuItem调用add()方法的方式,将它们添加到菜单中。这就使添加或减少菜单项时
不至于太乏味。


程序中不是创建了一个而是创建了两个JMenuBar,用以演示程序运行期间可以动态替换菜
单条。你可以看到如何用JMenu构造JMenuBar,以及用JMenuItem,
JCheckBoxMenuItem甚至JMenu(产生子菜单)来装配JMenu。当构造完一个JmenuBar
后,可以使用setJMenuBar( )方法把它安装到当前程序上。注意,当按钮按下的时候,它
将通过调用 getJMenuBar( )来判断当前安装的是哪一个菜单条,然后替换成另一个菜单
条。


在测试“Open”菜单项的时候,要注意到拼写和大小写是很关键的,如果没有任何匹配的
“Open”,Java也不会报告任何错误。这种类型的字符串比较是造成程序错误的根源之一。






菜单项的选中和清除能够被自动地处理。处理JCheckBoxMenuItem的代码演示了判断菜
单项是否选中的两种方式:字符串比较(如上所述,尽管能使用这种方法,但很不安全),
和比较事件的目标对象。可以使用getState( )方法得到是否选中的状态。你还可以用
setState( )方法来改变JCheckBoxMenuItem的状态。


菜单对应的事件有些不一致,这可能会引起困惑:JMenuItem使用的是ActionListener,
而JCheckBoxMenuItem使用的是ItemListener。JMenu虽然对象也支持
ActionListeners,不过其用处并不大。一般来说,你要把监听器关联到每一个JMenuItem,
JCheckBoxMenuItem, 或者JradioButtonMenuItem上,但是在上例中ItemListener
和ActionListener关联到了不同的菜单组件上。


Swing支持助记键,或者称为“键盘快捷键”,所以你可以使用键盘而不是鼠标来选择任
何从AbstractButton(按钮,菜单项等等)继承而来的组件。做到这一点很简单;只要使
用重载的构造器,使它的第二个参数接受快捷键的标识符即可。不过,大多数
AbstractButton没有这样的构造器,所以更通用作法的是使用setMnemonic( )方法。上
例中为按钮和部分菜单项添加了快捷键;快捷指示符会自动地出现在组件上。


你还能看到 setActionCommand( )的用法。它看起来有些奇怪,因为在每种情况下的“动
作命令”(action command)与菜单上的标签都完全相同。为什么不直接使用标签而是
这种额外的字符串呢?问题在于对国际化的支持。如果你要把程序以另一种语言发布,最
好是希望只改变菜单上的标签,而不用修改代码(毫无疑问,修改代码会引入新的错误)。
为了使代码能更容易地判断与菜单关联的字符串,可以把“动作命令”作为不变量,而把
菜单上的标签作为可变量。所有的代码在运行时都使用“动作命令”,这样改变菜单标签
的时候就不会影响代码。注意,在本例中,并非所有菜单都是基于“动作命令”进行判断
的,这是因为没有专门为它们设定“动作命令”。
 
大量工作都是在监听器中完成的。BL执行的是JMenuBar的交换。在ML中,采用了“找出
按铃者”方式,它的作法是先得到ActionEvent的事件源,然后把它类型转换成
JmenuItem,接着得到其“动作命令”的字符串,并且把它传递给级联的if语句进行处理。
尽管FL监听器处理的是风味菜单中所有不同风味的菜单项,但它的确很简单。如果你的事
件处理逻辑够简单的话,这种方式值得参考。不过一般情况下,你会采用在FooL,BarL和
BazL里面所使用的方式,它们只被关联到一个菜单项,所以就不需要进行额外的判断,因
为你明确知道是谁调用了监听器。尽管这种方式产生了更多的类,但是类内部的代码会更
短,整个处理过程也更安全。


你会发现,有关菜单的代码很快就变得冗长而且凌乱了。这时,使用 GUI 构造工具才是明
智的选择。好的工具还可以对菜单进行维护。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值