Swing图形界面之事件处理

组件事件处理

  GUI是基于事件驱动的,当用户与我们的程序互动时,就有了事件这个概念。例如:鼠标移动、单击按钮、输入字符......这些都是事件。GUI的事件信息存储在java.awt.AWTEvent的子类,常用的子类用ActionEventItemEventComponentEventContainerEventInputEventKeyEventMouseEvnent
  事件处理机制有3个组成部分:事件源事件对象事件监听者事件源是与用户交互的GUI组件;事件对象封装了关于GUI组件交互后的若干信息;事件监听者是某种监听者类的对象,当事件发生,事件源通知监听者。

在GUI程序设计中,程序员对事件处理必须做好俩件事:

  • 注册监听者和实现事件处理方法
  • 注册监听者可以采用事件源的addxxxListener()方法实现

例如:A.addxxxListener(B);
将B对象注册为A对象的监听者。当A发生xxx事件时,对象B能得到通知,并将调用相应方法处理该事件。

JButton 事件

  当用户用鼠标点击JButton对象时,仅仅产生一种ActionEvent事件。监听者必须自己实现ActionListener接口,并通过addActionListener()方法向事件源注册。ActionListener接口只有一个方法定义:

public interface ActionListener extends EventListener
{
    @Override
    public void actionPerformed(ActionEvent e);
}

监听者必须定义一个实现接口的类。

实验效果

import subJFrame;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonEventTest implements ActionListener {
    JFrame frame;
    Container contentPane;
    JButton button_1, button_2;
    JLabel label;

    @Override
    public void actionPerformed(ActionEvent e) {
        label.setText("You clicked Button: "+e.getActionCommand());
    }

    public ButtonEventTest(){
        frame = new subJFrame("ButtonEventTest");
        contentPane = frame.getContentPane();
        contentPane.setLayout(new BorderLayout(5,5));
        label = new JLabel(" "); // generate a none text label
        contentPane.add(label, BorderLayout.CENTER);
        button_1 = new JButton("A");
        button_2 = new JButton("B");
        contentPane.add(button_1, BorderLayout.NORTH);
        contentPane.add(button_2, BorderLayout.SOUTH);
        // make the buttons as the listener
        button_1.addActionListener(this);
        button_2.addActionListener(this);

        frame.setSize(300,200);
        frame.setVisible(true);
    }

    public static void main(String[] AUG) {
        new ButtonEventTest();
    }
}

运行效果:
JButton

解析

  actionPerformed()方法中的e.getActionCommand()是获取与此动作有关的命令名。button_1.addActionListener(this);是将当前对象(this指代的对象)注册为button_1对象的事件监听者,每当button_1对象被单击时,将调用actionPerformed()进行事件处理。

JTextField和JPasswordField

  JTextField和JPasswordField都是处理单行文本的,当在对象中输入数据并按了Enter键时,触发事件。当有多个事件源时,可以使用ActionEvent参数的getSource()方法来确定用户到底与哪一个GUI组件进行交互操作,通过这个方法可以获得事件产生的对象。

实验效果

import subJFrame;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class FieldTest {
    JFrame frame;
    Container contentpane;
    JTextField field_1, field_2, field_3;
    JPasswordField password;
    JLabel label;

    private class fieldEventHandler implements ActionListener{
        @Override
        public void actionPerformed(ActionEvent e) {
            String str = "";
            // judging if user enter the Enter Key
            if(e.getSource() == field_1)
                str = "Field_1: "+e.getActionCommand();
            else if(e.getSource() == field_2)
                str = "Field_2: "+e.getActionCommand();
            else if(e.getSource() == field_3)
                str = "Field_3: "+e.getActionCommand();
            else if(e.getSource() == password)
                str = "Password: "+e.getActionCommand();

            label.setText(str);
        }
    }

    public FieldTest(){
        frame = new subJFrame("FieldTest");
        contentpane = frame.getContentPane();
        contentpane.setLayout(new GridLayout(2,3,10,10));

        field_1 = new JTextField(10);
        field_2 = new JTextField(" Enter text here.");
        field_3 = new JTextField(" Uneditable this text.",20);
        field_3.setEditable(false);
        password = new JPasswordField(" Hide this text.",20);

        label = new JLabel("I am a label!");
        contentpane.add(field_1);
        contentpane.add(field_2);
        contentpane.add(field_3);
        contentpane.add(password);

        fieldEventHandler handler = new fieldEventHandler();
        field_1.addActionListener(handler);
        field_2.addActionListener(handler);
        field_3.addActionListener(handler);
        password.addActionListener(handler);
        frame.setSize(400,200);
        frame.setVisible(true);
    }

    public static void main(String[] AUG) {
        new FieldTest();
    }
}

运行实例:
FieldTest

解析

  • 程序引入内涵类。程序中fieldEventHandler类定义在主类FieldTest内部,这是一种定义内隐类的方法,它表明只能在FieldTest内部使用。
  • 在内隐类内部,可以使用主类FieldTest定义的实例变量。
  • 内隐类fieldEventHandler实现了ActionListener接口,这是因为后面的内隐类对象要处理文本框事件。
  • actionPerformed(ActionEvent e)方法中通过e.getActionCommand()方法可以获取文本框中的内容。
  • 在构造函数中,field_1.addActionListener(handler);是将handler注册为field_1对象的监听者。

JCheckBox和JRadioButton

  JCheckBox对象支持复选框,他的状态要么是on要么是off。JRadioButton专门用于单选,他与ButtonGroup类配合使用,该类用于将相互独立的JRadioButton组件构成一个组。
  当用户对这俩种类型的按钮操作时,都要产生选择项事件。在编程中必须实现接口ItemListener,并编写ItemStateChanged()方法,才能处理这种类型事件。

实验效果

import subJFrame;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class BoxRadioTest {
    JPanel jpanel_1, jpanel_2;
    JFrame frame;
    Container contentPane;
    JTextField field_1, field_2;
    JCheckBox backGroundBox, foreGroundBox;
    JRadioButton backGroundRadio, foreGroundRadio;
    ButtonGroup radioGroup;

    private class BoxEventHandler implements ItemListener{
        private Color defaultBackground, defaultForeground;

        public void itemStateChanged(ItemEvent e){
            if(e.getSource() == backGroundBox)
                if(e.getStateChange() == ItemEvent.SELECTED)
                    defaultBackground = Color.darkGray;
                else
                    defaultBackground = Color.lightGray;
            if(e.getSource() == foreGroundBox)
                if(e.getStateChange() == ItemEvent.SELECTED)
                    defaultForeground = Color.orange;
                else
                    defaultForeground = Color.yellow;

            field_1.setForeground(defaultForeground);
            field_1.setBackground(defaultBackground);
        }
    }

    private class RadioEventHandler implements ItemListener{
        private Color defaultBackground, defaultForeground;

        public void itemStateChanged(ItemEvent e){
            if(e.getSource() == backGroundRadio)
                defaultBackground = Color.darkGray;
            else
                defaultBackground = Color.lightGray;
            if(e.getSource() == foreGroundRadio)
                defaultForeground = Color.orange;
            else
                defaultForeground = Color.yellow;

            field_2.setForeground(defaultForeground);
            field_2.setBackground(defaultBackground);
        }
    }

    public BoxRadioTest(){
        frame = new subJFrame("BoxRadioTest");
        contentPane = frame.getContentPane();
        contentPane.setLayout(new FlowLayout());

        jpanel_1 = new JPanel();
        field_1 = new JTextField(" Field_1: Watch the foreground and background colors change");
        jpanel_1.add(field_1);
        backGroundBox = new JCheckBox(" background ");
        foreGroundBox = new JCheckBox(" foreground ");
        contentPane.add(field_1);
        contentPane.add(backGroundBox);
        contentPane.add(foreGroundBox);

        jpanel_2 = new JPanel();
        field_2 = new JTextField(" Field_2: Watch the foreground and background colors change");
        jpanel_2.add(field_2);
        backGroundRadio = new JRadioButton(" background ");
        foreGroundRadio = new JRadioButton(" foreground ");
        contentPane.add(field_2);
        contentPane.add(backGroundRadio);
        contentPane.add(foreGroundRadio);

        radioGroup = new ButtonGroup();
        radioGroup.add(backGroundRadio);
        radioGroup.add(foreGroundRadio);

        contentPane.add(jpanel_1,BorderLayout.NORTH);
        contentPane.add(jpanel_2,BorderLayout.SOUTH);

        BoxEventHandler boxHandler = new BoxEventHandler();
        backGroundBox.addItemListener(boxHandler);
        foreGroundBox.addItemListener(boxHandler);

        RadioEventHandler radioHandler = new RadioEventHandler();
        backGroundRadio.addItemListener(radioHandler);
        foreGroundRadio.addItemListener(radioHandler);

        frame.setSize(800,200);
        frame.setVisible(true);
    }

    public static void main(String[] AUG) {
        new BoxRadioTest();
    }
}

运行实例:
BoxRadio

解析

  首先定义jpanel_1和jpanel_2俩个面板,jpanel_1上分别放置了一个文本框对象field_1和俩个复选框对象,复选框可以控制前景色和背景色,点击复选框时,触发事件调用BoxEventHandler中的itemStateChanged()方法进行事件处理;jpanel_2上的原理和jpanel_1一样,只不过是单选按钮,俩个单选按钮构成一个组。

不能采用 add()方法将ButtonGroup对象加入到容器中,因为它不是Component类(或其子类)的对象。此外ButtonGroup也不是个在屏幕上可显示的组件,他不会产生任何事件,所以不需要事件处理,只能用JRadioButton对象进行管理。

JComboBox 事件处理

  该组件生成的下拉列表适用于GUI空间小的情况,可以将一组互斥的单选按钮换成一个紧凑的下拉列表组件。当在下拉列表中选择或输入可编辑的选项时,将触发ItemListener事件,监听者将调用itemStateChanged()方法进行事件处理。

实验效果

import subJFrame;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class JComboBoxTest {
    JFrame frame;
    Container contentPane;
    JComboBox imageComboBox;
    JLabel label,lll;
    String questions[] = {"Name", "Sex", "Nation", "Education"};
    String answers[] = {"CongTsang", "Man", "China", "Sophomore"};

    public JComboBoxTest(){
        frame = new subJFrame("JComboBoxTest");
        contentPane = frame.getContentPane();
        contentPane.setLayout(new FlowLayout());
        // generate a 3 cols list
        imageComboBox = new JComboBox(questions);
        imageComboBox.setMaximumRowCount(3);
        contentPane.add(imageComboBox);
        // default answer
        label = new JLabel(answers[0]);
        contentPane.add(label);
        // 下面是编写程序时常用方法
        imageComboBox.addItemListener(
                // 生成无名内隐类,处理JComboBox事件
                new ItemListener() {
                    @Override
                    public void itemStateChanged(ItemEvent e) {
                        if(e.getStateChange() == ItemEvent.SELECTED){
                            int i = imageComboBox.getSelectedIndex();
                            label.setText(answers[i]);
                        }
                    }
                }
        );
        frame.setSize(300,150);
        frame.setVisible(true);
    }

    public static void main(String[] AUG) {
        new JComboBoxTest();
    }
}

运行实例:
JComboBox

解析

  程序中定义了一个无名的内隐类对象,它实现了ItemListener接口。当JComboBox对象中选择一个问题,监听者将调用itemStateChange()方法。GetSelectIndex()方法返回JComboBox选项下标。
  程序首先定义了一个ActionListener类型的变量listener,并在赋值语句后面提供了定义,然后将listener作为一个监听者加入监听者列表:contaniner.addActionListener(listener);
  但是在编写程序时,可将定义匿名类和加入监听者列表放在一起,从而将局部变量listener省略。

container.addActionListener(
    new ActionListener(){
        public void actionPerformed(ActionEvent e){
            // code
        }
    }
); // don't forget this semicolon(;)

对于这种匿名类写法最好不要多用,随着类定义的不断扩大,采用匿名类是的代码可读性极速降低。慎用!

JList 事件处理

  该类是JComponent的一个子类,默认方式支持多选,并且必须放在JScrollPane中才具有滚动功能。

实验效果

import subJFrame;

import java.awt.*;
import javax.swing.event.*;
import javax.swing.*;

public class JListTest {
    JFrame frame;
    Container contentPane;
    JList list;
    JLabel label;

    String colorNames[] = {"Black", "Blue", "Red", "White", "Yellow"};
    Color colors[] = {Color.black, Color.blue, Color.red, Color.white, Color.yellow};

    public JListTest(){
        frame = new subJFrame("JListTest");
        contentPane = frame.getContentPane();
        contentPane.setLayout(new FlowLayout());

        // generate a 5 cols list
        list = new JList(colorNames);
        list.setVisibleRowCount(5);
        // for each time can only choose a single one
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        contentPane.add(new JScrollPane(list));

        label = new JLabel(" color ");  // default label text
        contentPane.add(label);

        list.addListSelectionListener(
                new ListSelectionListener() {
                    @Override
                    public void valueChanged(ListSelectionEvent e) {
                        label.setText(list.getSelectedValue().toString());
                        label.setForeground(colors[list.getSelectedIndex()]);
                    }
                }
        );
        frame.setSize(200,150);
        frame.setVisible(true);
    }

    public static void main(String[] AUG) {
        new JListTest();
    }
}

运行实例:
JList

解析

  程序采用JList的addListSelectionListener()方法将一个匿名类对象注册为list对象的监听者。匿名类实现的接口是ListSelectionListener,该接口包含了valueChanged()方法。当用户在list中选择条目时,将调用valueChanged()方法进行事件处理。
  JList中的setVisibleRowCount()方法是设置list对象一屏可以显示的项数。setSelectionMode() 方法设置list对象的选择模式。ListSelectionModel是一个类,定义在javax.swing包中,包含3个常量:

  • SINGLE_SELECTION:仅允许选择列表中一条
  • SINGLE_INTERVAL_SELECTION:允许选择列表中多条,并且这些条目必须是连续的,中间不断开
  • MULTIPLE_INTERVAL_SELECTION:允许选择列表中多条,条目可以不连续

程序中出现的几条语句解释一下:

  • label.setText(list.getSelectedValue().toString()):采用getSelectedValue()方法获取list对象的选择项,然后采用toString()方法将其转换为一个字符串,并用这个字符串设置标签label的显示文本
  • label.setForeground(colors[list.getSelectedIndex()]):首先采用getSelectedIndex()方法获取list对象的选择项的标号,然后采用颜色colors数组设置标签label的前景色

爱狂笑的孩子运气不会差