原文: http://zetcode.com/tutorials/javaswingtutorial/swingevents/

所有 GUI 应用都是事件驱动的。 应用会对在其生命周期内生成的不同事件类型做出反应。 事件主要由应用的用户生成,但也可以通过其他方式生成,例如互联网连接,窗口管理器或计时器。

Tweet

在事件模型中,有三个参与者:

  • 事件来源
  • 事件对象
  • 事件监听器

事件源是状态更改的对象。 它生成事件。事件对象(事件)将状态更改封装在事件源中。事件监听器是要通知的对象。 事件源对象将处理事件的任务委托给事件监听器。

Java Swing 工具箱中的事件处理功能非常强大且灵活。 Java 使用事件委托模型。 我们指定发生特定事件时要通知的对象。

事件对象

当应用发生故障时,将创建一个事件对象。 例如,当我们单击按钮或从列表中选择一个项目时。 事件有几种类型,包括ActionEventTextEventFocusEventComponentEvent。 它们每个都是在特定条件下创建的。

事件对象保存有关已发生事件的信息。 在下一个示例中,我们将更详细地分析ActionEvent

EventObjectEx.java

  1. package com.zetcode;
  2. import javax.swing.AbstractAction;
  3. import javax.swing.BorderFactory;
  4. import javax.swing.DefaultListModel;
  5. import javax.swing.GroupLayout;
  6. import javax.swing.JButton;
  7. import javax.swing.JComponent;
  8. import javax.swing.JFrame;
  9. import javax.swing.JList;
  10. import java.awt.EventQueue;
  11. import java.awt.event.ActionEvent;
  12. import java.time.Instant;
  13. import java.time.ZoneId;
  14. import java.time.format.DateTimeFormatter;
  15. public class EventObjectEx extends JFrame {
  16. private JList mylist;
  17. private DefaultListModel model;
  18. public EventObjectEx() {
  19. initUI();
  20. }
  21. private void initUI() {
  22. model = new DefaultListModel();
  23. mylist = new JList(model);
  24. mylist.setBorder(BorderFactory.createEtchedBorder());
  25. var okBtn = new JButton("OK");
  26. okBtn.addActionListener(new ClickAction());
  27. createLayout(okBtn, mylist);
  28. setTitle("Event object");
  29. setLocationRelativeTo(null);
  30. setDefaultCloseOperation(EXIT_ON_CLOSE);
  31. }
  32. private void createLayout(JComponent... arg) {
  33. var pane = getContentPane();
  34. var gl = new GroupLayout(pane);
  35. pane.setLayout(gl);
  36. gl.setAutoCreateContainerGaps(true);
  37. gl.setAutoCreateGaps(true);
  38. gl.setHorizontalGroup(gl.createSequentialGroup()
  39. .addComponent(arg[0])
  40. .addComponent(arg[1], 250, GroupLayout.PREFERRED_SIZE,
  41. GroupLayout.DEFAULT_SIZE)
  42. );
  43. gl.setVerticalGroup(gl.createParallelGroup()
  44. .addComponent(arg[0])
  45. .addComponent(arg[1], 150, GroupLayout.PREFERRED_SIZE,
  46. GroupLayout.DEFAULT_SIZE)
  47. );
  48. pack();
  49. }
  50. private class ClickAction extends AbstractAction {
  51. @Override
  52. public void actionPerformed(ActionEvent e) {
  53. var formatter = DateTimeFormatter.ISO_TIME;
  54. var localTime = Instant.ofEpochMilli(e.getWhen()).atZone(
  55. ZoneId.systemDefault()).toLocalTime();
  56. var text = localTime.format(formatter);
  57. if (!model.isEmpty()) {
  58. model.clear();
  59. }
  60. if (e.getID() == ActionEvent.ACTION_PERFORMED) {
  61. model.addElement("Event Id: ACTION_PERFORMED");
  62. }
  63. model.addElement("Time: " + text);
  64. var source = e.getSource().getClass().getName();
  65. model.addElement("Source: " + source);
  66. var mod = e.getModifiers();
  67. var buffer = new StringBuffer("Modifiers: ");
  68. if ((mod & ActionEvent.ALT_MASK) < 0) {
  69. buffer.append("Alt ");
  70. }
  71. if ((mod & ActionEvent.SHIFT_MASK) < 0) {
  72. buffer.append("Shift ");
  73. }
  74. if ((mod & ActionEvent.META_MASK) < 0) {
  75. buffer.append("Meta ");
  76. }
  77. if ((mod & ActionEvent.CTRL_MASK) < 0) {
  78. buffer.append("Ctrl ");
  79. }
  80. model.addElement(buffer);
  81. }
  82. }
  83. public static void main(String[] args) {
  84. EventQueue.invokeLater(() -> {
  85. var ex = new EventObjectEx();
  86. ex.setVisible(true);
  87. });
  88. }
  89. }

发生动作时,将调用actionPerformed()方法。 它的参数是ActionEvent对象。

  1. var formatter = DateTimeFormatter.ISO_TIME;
  2. var localTime = Instant.ofEpochMilli(e.getWhen()).atZone(
  3. ZoneId.systemDefault()).toLocalTime();
  4. var text = localTime.format(formatter);

我们获得事件发生的时间。 getWhen()方法返回以毫秒为单位的时间值。 我们将值转换为LocalTime并使用DateTimeFormatter将其格式化为 ISO 时间。

  1. var source = e.getSource().getClass().getName();
  2. model.addElement("Source: " + source);

在这里,我们将事件源的名称添加到列表中。 在我们的情况下,源是JButton

  1. var mod = e.getModifiers();

我们得到了修饰键。 它是修饰符常量的按位或。

  1. if ((mod & ActionEvent.SHIFT_MASK) > 0)
  2. buffer.append("Shift ");

在这里,我们确定是否按下了 Shift 键。

Java Swing 事件 - 图1

图:事件对象

事件处理的实现

我们有几种方法可以在 Java Swing 中实现事件处理:

  • 匿名内部类
  • 内部类
  • 派生类

匿名内部类

我们从一个匿名内部类开始。

AnonymousInnerClassEx.java

  1. package com.zetcode;
  2. import javax.swing.GroupLayout;
  3. import javax.swing.JButton;
  4. import javax.swing.JComponent;
  5. import javax.swing.JFrame;
  6. import java.awt.EventQueue;
  7. import java.awt.event.ActionEvent;
  8. import java.awt.event.ActionListener;
  9. public class AnonymousInnerClassEx extends JFrame {
  10. public AnonymousInnerClassEx() {
  11. initUI();
  12. }
  13. private void initUI() {
  14. var closeBtn = new JButton("Close");
  15. closeBtn.addActionListener(new ActionListener() {
  16. @Override
  17. public void actionPerformed(ActionEvent event) {
  18. System.exit(0);
  19. }
  20. });
  21. createLayout(closeBtn);
  22. setTitle("Anonymous inner class");
  23. setLocationRelativeTo(null);
  24. setDefaultCloseOperation(EXIT_ON_CLOSE);
  25. }
  26. private void createLayout(JComponent... arg) {
  27. var pane = getContentPane();
  28. var gl = new GroupLayout(pane);
  29. pane.setLayout(gl);
  30. gl.setAutoCreateContainerGaps(true);
  31. gl.setAutoCreateGaps(true);
  32. gl.setHorizontalGroup(gl.createSequentialGroup()
  33. .addComponent(arg[0])
  34. .addGap(220)
  35. );
  36. gl.setVerticalGroup(gl.createParallelGroup()
  37. .addComponent(arg[0])
  38. .addGap(220)
  39. );
  40. pack();
  41. }
  42. public static void main(String[] args) {
  43. EventQueue.invokeLater(() -> {
  44. var ex = new AnonymousInnerClassEx();
  45. ex.setVisible(true);
  46. });
  47. }
  48. }

在此示例中,我们有一个按钮,单击后会关闭窗口。

  1. var closeBtn = new JButton("Close");

“关闭”按钮是事件源。 它将生成事件。

  1. closeBtn.addActionListener(new ActionListener() {
  2. @Override
  3. public void actionPerformed(ActionEvent event) {
  4. System.exit(0);
  5. }
  6. });

在这里,我们使用按钮注册一个动作监听器。 事件被发送到事件目标。 在我们的案例中,事件目标是ActionListener类; 在此代码中,我们使用匿名内部类。

  1. closeBtn.addActionListener((ActionEvent event) -> {
  2. System.exit(0);
  3. });

使用 lambda 表达式重写的代码。

内部类

在这里,我们使用内部ActionListener类实现示例。

InnerClassEx.java

  1. package com.zetcode;
  2. import javax.swing.GroupLayout;
  3. import javax.swing.JButton;
  4. import javax.swing.JComponent;
  5. import javax.swing.JFrame;
  6. import java.awt.EventQueue;
  7. import java.awt.event.ActionEvent;
  8. import java.awt.event.ActionListener;
  9. public class InnerClassEx extends JFrame {
  10. public InnerClassEx() {
  11. initUI();
  12. }
  13. private void initUI() {
  14. var closeBtn = new JButton("Close");
  15. var listener = new ButtonCloseListener();
  16. closeBtn.addActionListener(listener);
  17. createLayout(closeBtn);
  18. setTitle("Inner class example");
  19. setLocationRelativeTo(null);
  20. setDefaultCloseOperation(EXIT_ON_CLOSE);
  21. }
  22. private void createLayout(JComponent... arg) {
  23. var pane = getContentPane();
  24. var gl = new GroupLayout(pane);
  25. pane.setLayout(gl);
  26. gl.setAutoCreateContainerGaps(true);
  27. gl.setAutoCreateGaps(true);
  28. gl.setHorizontalGroup(gl.createSequentialGroup()
  29. .addComponent(arg[0])
  30. .addGap(220)
  31. );
  32. gl.setVerticalGroup(gl.createParallelGroup()
  33. .addComponent(arg[0])
  34. .addGap(220)
  35. );
  36. pack();
  37. }
  38. private class ButtonCloseListener implements ActionListener {
  39. @Override
  40. public void actionPerformed(ActionEvent e) {
  41. System.exit(0);
  42. }
  43. }
  44. public static void main(String[] args) {
  45. EventQueue.invokeLater(() -> {
  46. var ex = new InnerClassEx();
  47. ex.setVisible(true);
  48. });
  49. }
  50. }

我们在面板上有一个关闭按钮。 它的监听器在一个命名的内部类中定义。

  1. var listener = new ButtonCloseListener();
  2. closeBtn.addActionListener(listener);

在这里,我们有一个非匿名内部类。

  1. private class ButtonCloseListener implements ActionListener {
  2. @Override
  3. public void actionPerformed(ActionEvent e) {
  4. System.exit(0);
  5. }
  6. }

在此定义了按钮监听器。

实现监听器的派生类

下面的示例将从组件派生一个类,并在该类内部实现一个动作监听器。

DerivedClassEx.java

  1. package com.zetcode;
  2. import javax.swing.GroupLayout;
  3. import javax.swing.JButton;
  4. import javax.swing.JComponent;
  5. import javax.swing.JFrame;
  6. import java.awt.EventQueue;
  7. import java.awt.event.ActionEvent;
  8. import java.awt.event.ActionListener;
  9. public class DerivedClassEx extends JFrame {
  10. public DerivedClassEx() {
  11. initUI();
  12. }
  13. private void initUI() {
  14. var closeBtn = new MyButton("Close");
  15. createLayout(closeBtn);
  16. setTitle("Derived class");
  17. setLocationRelativeTo(null);
  18. setDefaultCloseOperation(EXIT_ON_CLOSE);
  19. }
  20. private void createLayout(JComponent... arg) {
  21. var pane = getContentPane();
  22. var gl = new GroupLayout(pane);
  23. pane.setLayout(gl);
  24. gl.setAutoCreateContainerGaps(true);
  25. gl.setHorizontalGroup(gl.createSequentialGroup()
  26. .addComponent(arg[0])
  27. .addGap(220)
  28. );
  29. gl.setVerticalGroup(gl.createParallelGroup()
  30. .addComponent(arg[0])
  31. .addGap(220)
  32. );
  33. pack();
  34. }
  35. private class MyButton extends JButton implements ActionListener {
  36. public MyButton(String text) {
  37. super.setText(text);
  38. addActionListener(this);
  39. }
  40. @Override
  41. public void actionPerformed(ActionEvent e) {
  42. System.exit(0);
  43. }
  44. }
  45. public static void main(String[] args) {
  46. EventQueue.invokeLater(() -> {
  47. var ex = new DerivedClassEx();
  48. ex.setVisible(true);
  49. });
  50. }
  51. }

在此示例中,我们创建一个派生的MyButton类,该类实现了动作监听器。

  1. var closeButton = new MyButton("Close");

在这里,我们创建自定义MyButton类。

  1. private class MyButton extends JButton implements ActionListener {
  2. public MyButton(String text) {
  3. super.setText(text);
  4. addActionListener(this);
  5. }
  6. @Override
  7. public void actionPerformed(ActionEvent e) {
  8. System.exit(0);
  9. }
  10. }

MyButton类是从JButton类扩展的。 它实现了ActionListener接口。 这样,事件处理在MyButton类中进行管理。

多种来源

监听器可以插入多个源。 在下一个示例中将对此进行解释。

MultipleSourcesEx.java

  1. package com.zetcode;
  2. import javax.swing.BorderFactory;
  3. import javax.swing.GroupLayout;
  4. import javax.swing.JButton;
  5. import javax.swing.JComponent;
  6. import javax.swing.JFrame;
  7. import javax.swing.JLabel;
  8. import java.awt.EventQueue;
  9. import java.awt.event.ActionEvent;
  10. import java.awt.event.ActionListener;
  11. import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
  12. public class MultipleSourcesEx extends JFrame {
  13. private JLabel statusBar;
  14. public MultipleSourcesEx() {
  15. initUI();
  16. }
  17. private void initUI() {
  18. statusBar = new JLabel("Ready");
  19. statusBar.setBorder(BorderFactory.createEtchedBorder());
  20. var butListener = new ButtonListener();
  21. var closeBtn = new JButton("Close");
  22. closeBtn.addActionListener(butListener);
  23. var openBtn = new JButton("Open");
  24. openBtn.addActionListener(butListener);
  25. var findBtn = new JButton("Find");
  26. findBtn.addActionListener(butListener);
  27. var saveBtn = new JButton("Save");
  28. saveBtn.addActionListener(butListener);
  29. createLayout(closeBtn, openBtn, findBtn, saveBtn, statusBar);
  30. setTitle("Multiple Sources");
  31. setLocationRelativeTo(null);
  32. setDefaultCloseOperation(EXIT_ON_CLOSE);
  33. }
  34. private void createLayout(JComponent... arg) {
  35. var pane = getContentPane();
  36. var gl = new GroupLayout(pane);
  37. pane.setLayout(gl);
  38. gl.setAutoCreateContainerGaps(true);
  39. gl.setAutoCreateGaps(true);
  40. gl.setHorizontalGroup(gl.createParallelGroup()
  41. .addComponent(arg[0])
  42. .addComponent(arg[1])
  43. .addComponent(arg[2])
  44. .addComponent(arg[3])
  45. .addComponent(arg[4], GroupLayout.DEFAULT_SIZE,
  46. GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
  47. .addGap(250)
  48. );
  49. gl.setVerticalGroup(gl.createSequentialGroup()
  50. .addComponent(arg[0])
  51. .addComponent(arg[1])
  52. .addComponent(arg[2])
  53. .addComponent(arg[3])
  54. .addPreferredGap(RELATED,
  55. GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
  56. .addComponent(arg[4])
  57. );
  58. gl.linkSize(arg[0], arg[1], arg[2], arg[3]);
  59. pack();
  60. }
  61. private class ButtonListener implements ActionListener {
  62. @Override
  63. public void actionPerformed(ActionEvent e) {
  64. var o = (JButton) e.getSource();
  65. var label = o.getText();
  66. statusBar.setText(" " + label + " button clicked");
  67. }
  68. }
  69. public static void main(String[] args) {
  70. EventQueue.invokeLater(() -> {
  71. var ex = new MultipleSourcesEx();
  72. ex.setVisible(true);
  73. });
  74. }
  75. }

我们创建四个按钮和一个状态栏。 单击按钮后,状态栏将显示一条消息。

  1. var closeBtn = new JButton("Close");
  2. closeBtn.addActionListener(butListener);
  3. var openBtn = new JButton("Open");
  4. openBtn.addActionListener(butListener);
  5. ...

每个按钮注册相同的ButtonListener对象。

  1. private class ButtonListener implements ActionListener {
  2. @Override
  3. public void actionPerformed(ActionEvent e) {
  4. var o = (JButton) e.getSource();
  5. var label = o.getText();
  6. statusBar.setText(" " + label + " button clicked");
  7. }
  8. }

我们确定按下了哪个按钮,并为状态栏创建一条消息。 getSource()方法返回最初发生事件的对象。 该消息是用setText()方法设置的。

Java Swing 事件 - 图2

图:多个来源

多监听器

可以为一个事件注册多个监听器。

MultipleListenersEx.java

  1. package com.zetcode;
  2. import javax.swing.BorderFactory;
  3. import javax.swing.GroupLayout;
  4. import javax.swing.JButton;
  5. import javax.swing.JComponent;
  6. import javax.swing.JFrame;
  7. import javax.swing.JLabel;
  8. import javax.swing.JSpinner;
  9. import javax.swing.SpinnerNumberModel;
  10. import java.awt.EventQueue;
  11. import java.awt.event.ActionEvent;
  12. import java.awt.event.ActionListener;
  13. import java.time.Year;
  14. import static javax.swing.GroupLayout.Alignment.BASELINE;
  15. import static javax.swing.GroupLayout.DEFAULT_SIZE;
  16. import static javax.swing.GroupLayout.PREFERRED_SIZE;
  17. import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
  18. public class MultipleListenersEx extends JFrame {
  19. private JLabel statusBar;
  20. private JSpinner spinner;
  21. private int count = 0;
  22. public MultipleListenersEx() {
  23. initUI();
  24. }
  25. private void initUI() {
  26. statusBar = new JLabel("0");
  27. statusBar.setBorder(BorderFactory.createEtchedBorder());
  28. JButton addBtn = new JButton("+");
  29. addBtn.addActionListener(new ButtonListener1());
  30. addBtn.addActionListener(new ButtonListener2());
  31. int currentYear = Year.now().getValue();
  32. var yearModel = new SpinnerNumberModel(currentYear,
  33. currentYear - 100,
  34. currentYear + 100,
  35. 1);
  36. spinner = new JSpinner(yearModel);
  37. spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));
  38. createLayout(addBtn, spinner, statusBar);
  39. setTitle("Multiple Listeners");
  40. setLocationRelativeTo(null);
  41. setDefaultCloseOperation(EXIT_ON_CLOSE);
  42. }
  43. private void createLayout(JComponent... arg) {
  44. var pane = getContentPane();
  45. var gl = new GroupLayout(pane);
  46. pane.setLayout(gl);
  47. gl.setAutoCreateContainerGaps(true);
  48. gl.setHorizontalGroup(gl.createParallelGroup()
  49. .addGroup(gl.createSequentialGroup()
  50. .addComponent(arg[0])
  51. .addGap(20)
  52. .addComponent(arg[1], DEFAULT_SIZE,
  53. DEFAULT_SIZE, PREFERRED_SIZE))
  54. .addComponent(arg[2], GroupLayout.DEFAULT_SIZE,
  55. GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
  56. );
  57. gl.setVerticalGroup(gl.createSequentialGroup()
  58. .addGroup(gl.createParallelGroup(BASELINE)
  59. .addComponent(arg[0])
  60. .addGap(20)
  61. .addComponent(arg[1], DEFAULT_SIZE,
  62. DEFAULT_SIZE, PREFERRED_SIZE))
  63. .addPreferredGap(RELATED,
  64. GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
  65. .addComponent(arg[2])
  66. );
  67. pack();
  68. }
  69. private class ButtonListener1 implements ActionListener {
  70. @Override
  71. public void actionPerformed(ActionEvent e) {
  72. var val = (Integer) spinner.getValue();
  73. spinner.setValue(++val);
  74. }
  75. }
  76. private class ButtonListener2 implements ActionListener {
  77. @Override
  78. public void actionPerformed(ActionEvent e) {
  79. statusBar.setText(Integer.toString(++count));
  80. }
  81. }
  82. public static void main(String[] args) {
  83. EventQueue.invokeLater(() -> {
  84. var ex = new MultipleListenersEx();
  85. ex.setVisible(true);
  86. });
  87. }
  88. }

在此示例中,我们有一个按钮,一个微调器和一个状态栏。 对于一个事件,我们使用两个按钮监听器。 一键单击将为微调器组件增加一年并更新状态栏。 状态栏将显示我们单击按钮的次数。

  1. addBtn.addActionListener(new ButtonListener1());
  2. addBtn.addActionListener(new ButtonListener2());

我们注册了两个按钮监听器。

  1. var yearModel = new SpinnerNumberModel(currentYear,
  2. currentYear - 100,
  3. currentYear + 100,
  4. 1);
  5. spinner = new JSpinner(yearModel);

在这里,我们创建微调器组件。 我们使用微调器的年度模型。 SpinnerNumberModel参数是初始值,最小值和最大值以及步长。

  1. spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));

我们删除了千位分隔符。

  1. private class ButtonListener1 implements ActionListener {
  2. @Override
  3. public void actionPerformed(ActionEvent e) {
  4. var val = (Integer) spinner.getValue();
  5. spinner.setValue(++val);
  6. }
  7. }

第一个按钮监听器增加微调器组件的值。

  1. private class ButtonListener2 implements ActionListener {
  2. @Override
  3. public void actionPerformed(ActionEvent e) {
  4. statusBar.setText(Integer.toString(++count));
  5. }
  6. }

第二个按钮监听器增加状态栏的值。

Java Swing 事件 - 图3

图:多个监听器

删除监听器

可以使用removeActionListener()方法删除注册的监听器。 下面的示例演示了这一点。

RemoveListenerEx.java

  1. package com.zetcode;
  2. import javax.swing.GroupLayout;
  3. import javax.swing.JButton;
  4. import javax.swing.JCheckBox;
  5. import javax.swing.JComponent;
  6. import javax.swing.JFrame;
  7. import javax.swing.JLabel;
  8. import java.awt.EventQueue;
  9. import java.awt.event.ActionEvent;
  10. import java.awt.event.ActionListener;
  11. import java.awt.event.ItemEvent;
  12. public class RemoveListenerEx extends JFrame {
  13. private JLabel lbl;
  14. private JButton addBtn;
  15. private JCheckBox activeBox;
  16. private ButtonListener buttonlistener;
  17. private int count = 0;
  18. public RemoveListenerEx() {
  19. initUI();
  20. }
  21. private void initUI() {
  22. addBtn = new JButton("+");
  23. buttonlistener = new ButtonListener();
  24. activeBox = new JCheckBox("Active listener");
  25. activeBox.addItemListener((ItemEvent event) -> {
  26. if (activeBox.isSelected()) {
  27. addBtn.addActionListener(buttonlistener);
  28. } else {
  29. addBtn.removeActionListener(buttonlistener);
  30. }
  31. });
  32. lbl = new JLabel("0");
  33. createLayout(addBtn, activeBox, lbl);
  34. setTitle("Remove listener");
  35. setLocationRelativeTo(null);
  36. setDefaultCloseOperation(EXIT_ON_CLOSE);
  37. }
  38. private void createLayout(JComponent... arg) {
  39. var pane = getContentPane();
  40. var gl = new GroupLayout(pane);
  41. pane.setLayout(gl);
  42. gl.setAutoCreateContainerGaps(true);
  43. gl.setAutoCreateGaps(true);
  44. gl.setHorizontalGroup(gl.createSequentialGroup()
  45. .addGroup(gl.createParallelGroup()
  46. .addComponent(arg[0])
  47. .addComponent(arg[2]))
  48. .addGap(30)
  49. .addComponent(arg[1])
  50. );
  51. gl.setVerticalGroup(gl.createSequentialGroup()
  52. .addGroup(gl.createParallelGroup()
  53. .addComponent(arg[0])
  54. .addComponent(arg[1]))
  55. .addGap(30)
  56. .addComponent(arg[2])
  57. );
  58. pack();
  59. }
  60. private class ButtonListener implements ActionListener {
  61. @Override
  62. public void actionPerformed(ActionEvent e) {
  63. lbl.setText(Integer.toString(++count));
  64. }
  65. }
  66. public static void main(String[] args) {
  67. EventQueue.invokeLater(() -> {
  68. var ex = new RemoveListenerEx();
  69. ex.setVisible(true);
  70. });
  71. }
  72. }

面板上有三个组件:按钮,复选框和标签。 通过切换复选框,我们可以添加或删除按钮的监听器。

  1. buttonlistener = new ButtonListener();

如果要稍后删除它,我们必须创建一个非匿名监听器。

  1. activeBox.addItemListener((ItemEvent event) -> {
  2. if (activeBox.isSelected()) {
  3. addBtn.addActionListener(buttonlistener);
  4. } else {
  5. addBtn.removeActionListener(buttonlistener);
  6. }
  7. });

我们确定是否选中该复选框。 然后,我们添加或删除监听器。

Java Swing 事件 - 图4

图:删除监听器

移动窗口

以下示例将在屏幕上寻找窗口的位置。

MovingWindowEx.java

  1. package com.zetcode;
  2. import javax.swing.GroupLayout;
  3. import javax.swing.JComponent;
  4. import javax.swing.JFrame;
  5. import javax.swing.JLabel;
  6. import java.awt.EventQueue;
  7. import java.awt.Font;
  8. import java.awt.event.ComponentEvent;
  9. import java.awt.event.ComponentListener;
  10. public class MovingWindowEx extends JFrame implements ComponentListener {
  11. private JLabel labelx;
  12. private JLabel labely;
  13. public MovingWindowEx() {
  14. initUI();
  15. }
  16. private void initUI() {
  17. addComponentListener(this);
  18. labelx = new JLabel("x: ");
  19. labelx.setFont(new Font("Serif", Font.BOLD, 14));
  20. labelx.setBounds(20, 20, 60, 25);
  21. labely = new JLabel("y: ");
  22. labely.setFont(new Font("Serif", Font.BOLD, 14));
  23. labely.setBounds(20, 45, 60, 25);
  24. createLayout(labelx, labely);
  25. setTitle("Moving window");
  26. setLocationRelativeTo(null);
  27. setDefaultCloseOperation(EXIT_ON_CLOSE);
  28. }
  29. private void createLayout(JComponent... arg) {
  30. var pane = getContentPane();
  31. var gl = new GroupLayout(pane);
  32. pane.setLayout(gl);
  33. gl.setAutoCreateContainerGaps(true);
  34. gl.setAutoCreateGaps(true);
  35. gl.setHorizontalGroup(gl.createParallelGroup()
  36. .addComponent(arg[0])
  37. .addComponent(arg[1])
  38. .addGap(250)
  39. );
  40. gl.setVerticalGroup(gl.createSequentialGroup()
  41. .addComponent(arg[0])
  42. .addComponent(arg[1])
  43. .addGap(130)
  44. );
  45. pack();
  46. }
  47. @Override
  48. public void componentResized(ComponentEvent e) {
  49. }
  50. @Override
  51. public void componentMoved(ComponentEvent e) {
  52. var x = e.getComponent().getX();
  53. var y = e.getComponent().getY();
  54. labelx.setText("x: " + x);
  55. labely.setText("y: " + y);
  56. }
  57. @Override
  58. public void componentShown(ComponentEvent e) {
  59. }
  60. @Override
  61. public void componentHidden(ComponentEvent e) {
  62. }
  63. public static void main(String[] args) {
  64. EventQueue.invokeLater(() -> {
  65. var ex = new MovingWindowEx();
  66. ex.setVisible(true);
  67. });
  68. }
  69. }

该示例在面板上显示当前窗口坐标。 要获取窗口位置,我们使用ComponentListener

  1. public class MovingWindowExample extends JFrame implements ComponentListener {

主类实现ComponentListener接口。 它必须提供所有方法的实现。

  1. @Override
  2. public void componentResized(ComponentEvent e) {
  3. }
  4. @Override
  5. public void componentMoved(ComponentEvent e) {
  6. var x = e.getComponent().getX();
  7. var y = e.getComponent().getY();
  8. labelx.setText("x: " + x);
  9. labely.setText("y: " + y);
  10. }
  11. @Override
  12. public void componentShown(ComponentEvent e) {
  13. }
  14. @Override
  15. public void componentHidden(ComponentEvent e) {
  16. }

我们必须创建所有四种方法,即使我们对其中一种方法componentMoved()感兴趣。 其他三种方法为空。

  1. var x = e.getComponent().getX();
  2. var y = e.getComponent().getY();

在这里,我们获得了组件的 x 和 y 位置。

  1. labelx.setText("x: " + x);
  2. labely.setText("y: " + y);

检索到的值设置为标签。

Java Swing 事件 - 图5

图:移动窗口

适配器

适配器是一种方便的类,它为所有必需的方法提供空的实现。 在前面的代码示例中,即使没有使用它们,我们也必须实现ComponentListener类的所有四个方法。 为了避免不必要的编码,我们可以使用适配器。 然后,我们使用实现我们实际需要的那些方法。 没有用于按钮单击事件的适配器,因为只有一种方法可以实现actionPerformed()。 在有多种方法可以实现的情况下,可以使用适配器。

下面的示例是使用ComponentAdapter重写前一个示例。

AdapterEx.java

  1. package com.zetcode;
  2. import javax.swing.GroupLayout;
  3. import javax.swing.JComponent;
  4. import javax.swing.JFrame;
  5. import javax.swing.JLabel;
  6. import java.awt.EventQueue;
  7. import java.awt.Font;
  8. import java.awt.event.ComponentAdapter;
  9. import java.awt.event.ComponentEvent;
  10. public class AdapterEx extends JFrame {
  11. private JLabel labelx;
  12. private JLabel labely;
  13. public AdapterEx() {
  14. initUI();
  15. }
  16. private void initUI() {
  17. addComponentListener(new MoveAdapter());
  18. labelx = new JLabel("x: ");
  19. labelx.setFont(new Font("Serif", Font.BOLD, 14));
  20. labely = new JLabel("y: ");
  21. labely.setFont(new Font("Serif", Font.BOLD, 14));
  22. createLayout(labelx, labely);
  23. setTitle("Adapter example");
  24. setLocationRelativeTo(null);
  25. setDefaultCloseOperation(EXIT_ON_CLOSE);
  26. }
  27. private void createLayout(JComponent... arg) {
  28. var pane = getContentPane();
  29. var gl = new GroupLayout(pane);
  30. pane.setLayout(gl);
  31. gl.setAutoCreateContainerGaps(true);
  32. gl.setAutoCreateGaps(true);
  33. gl.setHorizontalGroup(gl.createParallelGroup()
  34. .addComponent(arg[0])
  35. .addComponent(arg[1])
  36. .addGap(250)
  37. );
  38. gl.setVerticalGroup(gl.createSequentialGroup()
  39. .addComponent(arg[0])
  40. .addComponent(arg[1])
  41. .addGap(130)
  42. );
  43. pack();
  44. }
  45. private class MoveAdapter extends ComponentAdapter {
  46. @Override
  47. public void componentMoved(ComponentEvent e) {
  48. var x = e.getComponent().getX();
  49. var y = e.getComponent().getY();
  50. labelx.setText("x: " + x);
  51. labely.setText("y: " + y);
  52. }
  53. }
  54. public static void main(String[] args) {
  55. EventQueue.invokeLater(() -> {
  56. var ex = new AdapterEx();
  57. ex.setVisible(true);
  58. });
  59. }
  60. }

此示例是对上一个示例的重写。 在这里,我们使用ComponentAdapter

  1. addComponentListener(new MoveAdapter());

在这里,我们注册组件监听器。

  1. private class MoveAdapter extends ComponentAdapter {
  2. @Override
  3. public void componentMoved(ComponentEvent e) {
  4. var x = e.getComponent().getX();
  5. var y = e.getComponent().getY();
  6. labelx.setText("x: " + x);
  7. labely.setText("y: " + y);
  8. }
  9. }

MoveAdapter内部类中,我们定义componentMoved()方法。 其他所有方法均保留为空。

Java Swing 教程的这一部分专门用于 Swing 事件。 我们介绍了事件源,事件对象,事件监听器,创建事件处理器的几种方法,多个源和监听器,删除监听器以及事件适配器。