Java Swing 事件
最后修改于 2023 年 1 月 10 日
所有 GUI 应用程序都是事件驱动的。应用程序对在其生命周期中生成的不同类型的事件做出反应。事件主要由应用程序用户生成,但也可以通过其他方式生成,例如 Internet 连接、窗口管理器或计时器。
在事件模型中,有三个参与者
- 事件源
- 事件对象
- 事件监听器
事件源是状态发生变化的那个对象。它会生成事件。事件对象(Event)封装了事件源的状态变化。事件监听器是希望被通知的对象。事件源对象将处理事件的任务委托给事件监听器。
Java Swing 工具包中的事件处理非常强大和灵活。Java 使用事件委托模型。我们指定当特定事件发生时要通知的对象。
Java Swing 事件对象
当应用程序中发生某事时,会创建一个事件对象。例如,当我们单击按钮或从列表中选择一项时。有几种类型的事件,包括 ActionEvent
、TextEvent
、FocusEvent
和 ComponentEvent
。每种事件都是在特定条件下创建的。
事件对象包含有关已发生事件的信息。在下一个示例中,我们将更详细地分析 ActionEvent
。
package com.zetcode; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JList; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; public class EventObjectEx extends JFrame { private JList mylist; private DefaultListModel model; public EventObjectEx() { initUI(); } private void initUI() { model = new DefaultListModel(); mylist = new JList(model); mylist.setBorder(BorderFactory.createEtchedBorder()); var okBtn = new JButton("OK"); okBtn.addActionListener(new ClickAction()); createLayout(okBtn, mylist); setTitle("Event object"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addComponent(arg[1], 250, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE) ); gl.setVerticalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addComponent(arg[1], 150, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE) ); pack(); } private class ClickAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { var formatter = DateTimeFormatter.ISO_TIME; var localTime = Instant.ofEpochMilli(e.getWhen()).atZone( ZoneId.systemDefault()).toLocalTime(); var text = localTime.format(formatter); if (!model.isEmpty()) { model.clear(); } if (e.getID() == ActionEvent.ACTION_PERFORMED) { model.addElement("Event Id: ACTION_PERFORMED"); } model.addElement("Time: " + text); var source = e.getSource().getClass().getName(); model.addElement("Source: " + source); var mod = e.getModifiers(); var buffer = new StringBuffer("Modifiers: "); if ((mod & ActionEvent.ALT_MASK) < 0) { buffer.append("Alt "); } if ((mod & ActionEvent.SHIFT_MASK) < 0) { buffer.append("Shift "); } if ((mod & ActionEvent.META_MASK) < 0) { buffer.append("Meta "); } if ((mod & ActionEvent.CTRL_MASK) < 0) { buffer.append("Ctrl "); } model.addElement(buffer); } } public static void main(String[] args) { EventQueue.invokeLater(() -< { var ex = new EventObjectEx(); ex.setVisible(true); }); } }
当发生事件时,会调用 actionPerformed()
方法。它的参数是 ActionEvent
对象。
var formatter = DateTimeFormatter.ISO_TIME; var localTime = Instant.ofEpochMilli(e.getWhen()).atZone( ZoneId.systemDefault()).toLocalTime(); var text = localTime.format(formatter);
我们获取事件发生的时间。getWhen()
方法返回毫秒值。我们将该值转换为 LocalTime
并使用 DateTimeFormatter
将其格式化为 ISO 时间。
var source = e.getSource().getClass().getName(); model.addElement("Source: " + source);
在这里,我们将事件源的名称添加到列表中。在我们的例子中,源是 JButton
。
var mod = e.getModifiers();
我们获取修饰键。它是修饰符常量的位或。
if ((mod & ActionEvent.SHIFT_MASK) > 0) buffer.append("Shift ");
在这里,我们确定是否按下了 Shift 键。

事件处理的实现
在 Java Swing 中实现事件处理有几种方法
- 匿名内部类
- 内部类
- 派生类
匿名内部类
我们从匿名内部类开始。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class AnonymousInnerClassEx extends JFrame { public AnonymousInnerClassEx() { initUI(); } private void initUI() { var closeBtn = new JButton("Close"); closeBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { System.exit(0); } }); createLayout(closeBtn); setTitle("Anonymous inner class"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(220) ); gl.setVerticalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addGap(220) ); pack(); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new AnonymousInnerClassEx(); ex.setVisible(true); }); } }
在这个例子中,我们有一个按钮,单击时会关闭窗口。
var closeBtn = new JButton("Close");
关闭按钮是事件源。它将生成事件。
closeBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { System.exit(0); } });
在这里,我们将一个操作监听器注册到按钮。事件被发送到事件目标。在我们的例子中,事件目标是 ActionListener
类;在此代码中,我们使用匿名内部类。
closeBtn.addActionListener((ActionEvent event) -> { System.exit(0); });
代码使用 lambda 表达式重写。
内部类
在这里,我们使用内部 ActionListener
类实现该示例。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class InnerClassEx extends JFrame { public InnerClassEx() { initUI(); } private void initUI() { var closeBtn = new JButton("Close"); var listener = new ButtonCloseListener(); closeBtn.addActionListener(listener); createLayout(closeBtn); setTitle("Inner class example"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(220) ); gl.setVerticalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addGap(220) ); pack(); } private class ButtonCloseListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new InnerClassEx(); ex.setVisible(true); }); } }
我们在面板上有一个关闭按钮。它的监听器定义在一个命名的内部类中。
var listener = new ButtonCloseListener(); closeBtn.addActionListener(listener);
这里我们有一个非匿名的内部类。
private class ButtonCloseListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }
按钮监听器在此处定义。
实现监听器的派生类
以下示例将从组件派生一个类,并在类中实现操作监听器。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class DerivedClassEx extends JFrame { public DerivedClassEx() { initUI(); } private void initUI() { var closeBtn = new MyButton("Close"); createLayout(closeBtn); setTitle("Derived class"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(220) ); gl.setVerticalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addGap(220) ); pack(); } private class MyButton extends JButton implements ActionListener { public MyButton(String text) { super.setText(text); addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { System.exit(0); } } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new DerivedClassEx(); ex.setVisible(true); }); } }
在这个例子中,我们创建了一个派生的 MyButton 类,它实现了 action listener。
var closeButton = new MyButton("Close");
这里我们创建自定义的 MyButton 类。
private class MyButton extends JButton implements ActionListener { public MyButton(String text) { super.setText(text); addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }
MyButton
类继承自 JButton
类。它实现了 ActionListener
接口。这样,事件处理就在 MyButton
类中进行了管理。
Java Swing 多个事件源
一个监听器可以接入多个源。这将在下一个示例中进行解释。
package com.zetcode; import javax.swing.BorderFactory; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; public class MultipleSourcesEx extends JFrame { private JLabel statusBar; public MultipleSourcesEx() { initUI(); } private void initUI() { statusBar = new JLabel("Ready"); statusBar.setBorder(BorderFactory.createEtchedBorder()); var butListener = new ButtonListener(); var closeBtn = new JButton("Close"); closeBtn.addActionListener(butListener); var openBtn = new JButton("Open"); openBtn.addActionListener(butListener); var findBtn = new JButton("Find"); findBtn.addActionListener(butListener); var saveBtn = new JButton("Save"); saveBtn.addActionListener(butListener); createLayout(closeBtn, openBtn, findBtn, saveBtn, statusBar); setTitle("Multiple Sources"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addComponent(arg[1]) .addComponent(arg[2]) .addComponent(arg[3]) .addComponent(arg[4], GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(250) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addComponent(arg[1]) .addComponent(arg[2]) .addComponent(arg[3]) .addPreferredGap(RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(arg[4]) ); gl.linkSize(arg[0], arg[1], arg[2], arg[3]); pack(); } private class ButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { var o = (JButton) e.getSource(); var label = o.getText(); statusBar.setText(" " + label + " button clicked"); } } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new MultipleSourcesEx(); ex.setVisible(true); }); } }
我们创建了四个按钮和一个状态栏。单击按钮时,状态栏将显示一条消息。
var closeBtn = new JButton("Close"); closeBtn.addActionListener(butListener); var openBtn = new JButton("Open"); openBtn.addActionListener(butListener); ...
每个按钮都注册了相同的 ButtonListener
对象。
private class ButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { var o = (JButton) e.getSource(); var label = o.getText(); statusBar.setText(" " + label + " button clicked"); } }
我们确定哪个按钮被按下,并为状态栏创建一条消息。getSource()
方法返回事件最初发生的那个对象。消息使用 setText()
方法设置。

Java Swing 多个监听器
可以为同一个事件注册多个监听器。
package com.zetcode; import javax.swing.BorderFactory; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.time.Year; import static javax.swing.GroupLayout.Alignment.BASELINE; import static javax.swing.GroupLayout.DEFAULT_SIZE; import static javax.swing.GroupLayout.PREFERRED_SIZE; import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; public class MultipleListenersEx extends JFrame { private JLabel statusBar; private JSpinner spinner; private int count = 0; public MultipleListenersEx() { initUI(); } private void initUI() { statusBar = new JLabel("0"); statusBar.setBorder(BorderFactory.createEtchedBorder()); JButton addBtn = new JButton("+"); addBtn.addActionListener(new ButtonListener1()); addBtn.addActionListener(new ButtonListener2()); int currentYear = Year.now().getValue(); var yearModel = new SpinnerNumberModel(currentYear, currentYear - 100, currentYear + 100, 1); spinner = new JSpinner(yearModel); spinner.setEditor(new JSpinner.NumberEditor(spinner, "#")); createLayout(addBtn, spinner, statusBar); setTitle("Multiple Listeners"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setHorizontalGroup(gl.createParallelGroup() .addGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(20) .addComponent(arg[1], DEFAULT_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)) .addComponent(arg[2], GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); gl.setVerticalGroup(gl.createSequentialGroup() .addGroup(gl.createParallelGroup(BASELINE) .addComponent(arg[0]) .addGap(20) .addComponent(arg[1], DEFAULT_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)) .addPreferredGap(RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(arg[2]) ); pack(); } private class ButtonListener1 implements ActionListener { @Override public void actionPerformed(ActionEvent e) { var val = (Integer) spinner.getValue(); spinner.setValue(++val); } } private class ButtonListener2 implements ActionListener { @Override public void actionPerformed(ActionEvent e) { statusBar.setText(Integer.toString(++count)); } } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new MultipleListenersEx(); ex.setVisible(true); }); } }
在这个例子中,我们有一个按钮、一个旋转框和一个状态栏。我们为同一个事件使用了两个按钮监听器。单击按钮一次会将年份增加到旋转框组件,并更新状态栏。状态栏将显示我们单击按钮的次数。
addBtn.addActionListener(new ButtonListener1()); addBtn.addActionListener(new ButtonListener2());
我们注册了两个按钮监听器。
var yearModel = new SpinnerNumberModel(currentYear, currentYear - 100, currentYear + 100, 1); spinner = new JSpinner(yearModel);
这里我们创建了旋转框组件。我们为旋转框使用了年份模型。SpinnerNumberModel
的参数是初始值、最小值、最大值和步长。
spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));
我们移除了千位分隔符。
private class ButtonListener1 implements ActionListener { @Override public void actionPerformed(ActionEvent e) { var val = (Integer) spinner.getValue(); spinner.setValue(++val); } }
第一个按钮监听器增加了旋转框组件的值。
private class ButtonListener2 implements ActionListener { @Override public void actionPerformed(ActionEvent e) { statusBar.setText(Integer.toString(++count)); } }
第二个按钮监听器增加了状态栏的值。

Java Swing 移除监听器
可以使用 removeActionListener()
方法移除已注册的监听器。以下示例对此进行了演示。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; public class RemoveListenerEx extends JFrame { private JLabel lbl; private JButton addBtn; private JCheckBox activeBox; private ButtonListener buttonlistener; private int count = 0; public RemoveListenerEx() { initUI(); } private void initUI() { addBtn = new JButton("+"); buttonlistener = new ButtonListener(); activeBox = new JCheckBox("Active listener"); activeBox.addItemListener((ItemEvent event) -> { if (activeBox.isSelected()) { addBtn.addActionListener(buttonlistener); } else { addBtn.removeActionListener(buttonlistener); } }); lbl = new JLabel("0"); createLayout(addBtn, activeBox, lbl); setTitle("Remove listener"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addGroup(gl.createParallelGroup() .addComponent(arg[0]) .addComponent(arg[2])) .addGap(30) .addComponent(arg[1]) ); gl.setVerticalGroup(gl.createSequentialGroup() .addGroup(gl.createParallelGroup() .addComponent(arg[0]) .addComponent(arg[1])) .addGap(30) .addComponent(arg[2]) ); pack(); } private class ButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { lbl.setText(Integer.toString(++count)); } } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new RemoveListenerEx(); ex.setVisible(true); }); } }
我们在面板上有三个组件:一个按钮、一个复选框和一个标签。通过切换复选框,我们可以添加或删除按钮的监听器。
buttonlistener = new ButtonListener();
如果我们要稍后移除监听器,我们必须创建一个非匿名的监听器。
activeBox.addItemListener((ItemEvent event) -> { if (activeBox.isSelected()) { addBtn.addActionListener(buttonlistener); } else { addBtn.removeActionListener(buttonlistener); } });
我们确定复选框是否被选中。然后我们添加或移除监听器。

Java Swing 移动窗口
以下示例将查找窗口在屏幕上的位置。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; public class MovingWindowEx extends JFrame implements ComponentListener { private JLabel labelx; private JLabel labely; public MovingWindowEx() { initUI(); } private void initUI() { addComponentListener(this); labelx = new JLabel("x: "); labelx.setFont(new Font("Serif", Font.BOLD, 14)); labelx.setBounds(20, 20, 60, 25); labely = new JLabel("y: "); labely.setFont(new Font("Serif", Font.BOLD, 14)); labely.setBounds(20, 45, 60, 25); createLayout(labelx, labely); setTitle("Moving window"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addComponent(arg[1]) .addGap(250) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addComponent(arg[1]) .addGap(130) ); pack(); } @Override public void componentResized(ComponentEvent e) { } @Override public void componentMoved(ComponentEvent e) { var x = e.getComponent().getX(); var y = e.getComponent().getY(); labelx.setText("x: " + x); labely.setText("y: " + y); } @Override public void componentShown(ComponentEvent e) { } @Override public void componentHidden(ComponentEvent e) { } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new MovingWindowEx(); ex.setVisible(true); }); } }
该示例显示了面板上窗口的当前坐标。要获取窗口位置,我们使用 ComponentListener
。
public class MovingWindowExample extends JFrame implements ComponentListener {
主类实现了 ComponentListener
接口。它必须提供其所有方法的实现。
@Override public void componentResized(ComponentEvent e) { } @Override public void componentMoved(ComponentEvent e) { var x = e.getComponent().getX(); var y = e.getComponent().getY(); labelx.setText("x: " + x); labely.setText("y: " + y); } @Override public void componentShown(ComponentEvent e) { } @Override public void componentHidden(ComponentEvent e) { }
我们必须创建所有四个方法,即使我们只对其中一个——componentMoved()
感兴趣。其他三个方法是空的。
var x = e.getComponent().getX(); var y = e.getComponent().getY();
这里我们获取组件的 x 和 y 位置。
labelx.setText("x: " + x); labely.setText("y: " + y);
检索到的值被设置为标签。

适配器
适配器是一个方便的类,它提供了所有必需方法为空的实现。在前面的代码示例中,我们必须实现 ComponentListener
类的所有四个方法——即使我们没有使用它们。为了避免不必要的编码,我们可以使用适配器。然后我们实现我们实际需要的方法。没有针对按钮点击事件的适配器,因为我们只有一个方法需要实现——actionPerformed()
。当需要实现多个方法时,我们可以使用适配器。
以下示例是重写前面的示例,使用了 ComponentAdapter
。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; public class AdapterEx extends JFrame { private JLabel labelx; private JLabel labely; public AdapterEx() { initUI(); } private void initUI() { addComponentListener(new MoveAdapter()); labelx = new JLabel("x: "); labelx.setFont(new Font("Serif", Font.BOLD, 14)); labely = new JLabel("y: "); labely.setFont(new Font("Serif", Font.BOLD, 14)); createLayout(labelx, labely); setTitle("Adapter example"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addComponent(arg[1]) .addGap(250) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addComponent(arg[1]) .addGap(130) ); pack(); } private class MoveAdapter extends ComponentAdapter { @Override public void componentMoved(ComponentEvent e) { var x = e.getComponent().getX(); var y = e.getComponent().getY(); labelx.setText("x: " + x); labely.setText("y: " + y); } } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new AdapterEx(); ex.setVisible(true); }); } }
此示例是重写前面的示例。这里我们使用了 ComponentAdapter
。
addComponentListener(new MoveAdapter());
在这里,我们注册了组件监听器。
private class MoveAdapter extends ComponentAdapter { @Override public void componentMoved(ComponentEvent e) { var x = e.getComponent().getX(); var y = e.getComponent().getY(); labelx.setText("x: " + x); labely.setText("y: " + y); } }
在 MoveAdapter
内部类中,我们定义了 componentMoved()
方法。所有其他方法都留空。
Java Swing 教程的这部分专门介绍了 Swing 事件。我们已经涵盖了事件源、事件对象、事件监听器、创建事件处理器的几种方法、多个源和监听器、移除监听器和事件适配器。