Java当中有两个GUI库,AWT和Swing。AWT是用非Java代码编写的库,所以对于不同的JVM有unconsistent的效果。但Swing作为一个Java编写的,不存在这个问题。但是Swing当中的事件控制,依然使用的AWT的库。
AWT的引用:java.awt.
Swing: javax.swing.
Java的Swing和Servlet一样,都是使用Cook Book Programming.
Recipe:
1/ Create Window Object (JFrame); BTW,通常J开头的都是Swing的类。
2/ Create Container Object (JPanel);
3/ 往这个Container里面加元素(JLabel, JButton, JTextArea, JCheckBox, JChoice, JRadioButton, JTextField, JTextArea, JList, JSrollBar, etc);
4/ 链接container和window (JframeObject.setContentPane(JpanelObject))
控制JPanel的Lay out:
一般介绍三种Lay out: FlowLayout (default), BorderLayout and GridLayout.
FlowLayout:将你加入到container的元素从左到右地排列,如果当前行满了不够放,则新开一行。
Border Layout: 把元素加入上、下、左、右和中,5个区域。
Grid Layout: 为每个元素定大小,变为等大小的网格。一开始就要指定行数和列数。
如何人为控制Lay out:
自己新建一个panel,然后手工加入到最早的panel当中
JPanel pane = new JPanel();
JPanel line = new JPanel();
JButton button = new JButton(new ImageIcon("obama.jpg"));
line.add(button);
JButton button2 = new JButton(new ImageIcon("trump.jpg"));
line.add(button2);
pane.add(line);
Swing当中的字体控制(用awt):
java.awt.Font bigFont = new Font(Font.SERIF, Font.BOLD, 20);
JLabelObject,setFont(bigFont);
Swing的Action监听:
Action的监听是UI设计的核心。如何一个UI无法通过Action监听获取用户的行为,那么这个UI将毫无意义。Swing 的Event listener(实际是AWT的)有以下:
ActionListener, AdjustmentListener, FocusListener, ItemListener, KeyListener, MouseListener...
ActionListener主要监听JButtons and JTextFields等等我们常用的元素。使用这个Listener需要建立一个ActionListener的实现类。实现其中的actionPerformed()的抽象方法。一般会在这个方法的实现当中调用event.getSource()或者event.getActionCommand()来确定是来自哪个component的动作。
实现例子:
* Method to be invoked when buttons are clicked.
* @param event event object
*/
@Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() == obamaButton) {
textArea.append("Yes we can!\n");
return;
}
if (event.getSource() == trumpButton) {
textArea.append("You're fired!\n");
return;
}
throw new AssertionError("Unknown event.");
}
最后,别忘了把这个ActionListener和对应的component绑定。(e.g.: obamaButton.addActionListener(this);)
Swing的组织(组合上述的代码):
两个建议:1) actionPerformed里面的代码不要写太长,写一些helper函数,显得代码容易读懂;2)对于要在actionPerformed里面调用的component,把它们声明为对象成员,便于调用,其余可以继续作为方法里面的临时变量。在Eclipse里面,Brown是临时变量,而Blue是对象成员。
组织Key One: 把Recipe放到哪里?
首先,很自然的想到的是都放进main函数里。然而,放在main函数有一个很大的drawback,便是作为一个static函数,无法调用非static的成员。在实现ActionListener的时候很有困难,十分不建议。
那么还存在另外的两种:1)创建一个类,类的构造器完成上述recipe的内容;2)创建一个JFrame的子类,然后除了JFrame,构造器完成剩下的recipe的内容。个人比较Prefer前一种。所以,在这里讲第一种。
组织Key Two: 如何实现ActionListener?
1)直接把上述创建的类implements ActionListener,然后在类里面实现actionPerformed方法即可。但是这样有个不好的地方,整个类当中,只能有一个ActionListener了。在某些应用场景,这是不够的;
2) 在上述创建的类当中创建一个内部类(nested class),内部类分为static的或者非static的。如果用static的内部类,无法调用对象成员,只能把对象成员作为构造器方法的参数传进去方法进行处理:
static的处理方法:
private static class ObamaActionListener implements ActionListener {
private JTextArea area;
public ObamaActionListener(JTextArea a) {
area = a;
}
public void actionPerformed(ActionEvent event) {
area.append("Yes we can!\n");
}
}
有同学可能会问,为什么不能把Component变为类的static成员呢?因为这与我们的设计初衷相悖了。我们这里需要每个类的对象代表一个新的窗口。如果我们类的对象(窗口)都共享相同Component的话,会有什么后果?敲黑板,这很重要哦。
非static版本的处理方法:
如果是非static的内部类的话,很明显不存在无法调用对象成员的问题。但是,需要注意的是,调用这个内部类需要先初始化一个对象。否则,是无法用到这个内部类的方法的。
所以应该使用这样的style:
public class QuoteGUIAction1b {
......
public QuoteGUIAction1b() {
.......
// Instantiate an ActionLister
ActionListener listener = new MyActionListener();
obamaButton.addActionListener(listener);
......
}
private class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
if (event.getSource() == obamaButton) {
textArea.append("Yes we can!\n");
return;
}
if (event.getSource() == trumpButton) {
textArea.append("You're fired!\n");
return;
}
throw new AssertionError("Unknown event.");
}
}
}
3) 当然,如果觉得每个Listener都要创建一个内部类太麻烦,可以创建匿名类:
// anonymous class that implements ActionListener
obamaButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.append("Yes we can!\n");
}
});
// anonymous class that implements ActionListener
trumpButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.append("You're fired!\n");
}
});