响应式 Swing 应用程序
在本节中,我们将创建一个应用程序,该应用程序响应用户操作,即按下按钮。
在上一节中,我们编写了一个程序,该程序显示了一个文本标签和一个按钮。在本节中,我们将向应用程序添加功能,以便当用户按下按钮时文本发生变化。为了实现这一点,我们将编写一个 JLabel
的子类,它可以更改其文本,并实现 ActionListener
接口。因为 JButton 在按下时会生成动作事件,所以我们可以通过将我们的专用 JLabel 注册为监听器来将其连接到 JButton。以下是代码
![]() |
新的监听器
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.SwingConstants;
import javax.swing.JLabel;
// ActionEvent and ActionListener
// live in the java.awt.event
// package
class ChangingLabel extends JLabel implements ActionListener {
// String constants which define the text that's displayed.
private static final String TEXT1 = "Hello, World!";
private static final String TEXT2 = "Hello again!";
// Instance field to keep track of the text currently
// displayed
private boolean usingText1;
// Constructor. Sets alignment and initial text, and
// sets the usingText1 field appropriately.
public ChangingLabel() {
this.setHorizontalAlignment(SwingConstants.CENTER);
this.setText(TEXT1);
usingText1 = true;
}
// A method to change the label text. If the
// current text is TEXT1, changes it to TEXT2,
// and vice-versa. The method is private here,
// because it is never called directly.
private void changeLabel() {
if(usingText1)
this.setText(TEXT2);
else
this.setText(TEXT1);
usingText1 = !usingText1;
}
// This method implements the ActionListener interface.
// Any time that an action is performed by a component
// that this object is registered with, this method
// is called. We can analyze the event object received
// here for more information if we want to, but it's not
// necessary in this application.
public void actionPerformed(ActionEvent e) {
this.changeLabel();
}
}
|
![]() |
具有活动按钮的 Swing 应用程序
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
// Application with a changing text field
public class HelloWithButton2 {
public static void main(String[] args) {
// Constructs the ChangingLabel. All the
// intialization is done be the default
// constructor, defined in the ChangingLabel
// class below.
ChangingLabel changingLabel = new ChangingLabel();
// Create the button
JButton button = new JButton("Button");
// Register the ChangingLabel as an action
// listener to the button. Whenever the
// button is pressed, its ActionEvent will
// be sent the ChangingLabel's
// actionPerformed() method, and the code
// there will be executed.
button.addActionListener(changingLabel);
// Create the frame
JFrame frame = new JFrame("Hello");
// Add the label and the button to the
// frame, using layout constants.
frame.add(changingLabel, BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.toFront();
}
}
|
在此应用程序中,类 ChangingLabel
定义了一种特殊类型的标签,该标签通过实现 ActionListener
接口成为动作监听器(即可以接收动作事件)。它还可以通过私有方法 changeLabel()
更改其文本。actionPerformed()
方法在收到动作事件时只是调用 changeLabel()
。
HelloWithButton2
中的程序主代码与上一节中的代码类似,但使用的是 ChangeLabel
而不是普通的 JLabel
对象。它还使用按钮的 addActionListener()
方法将 ChangeLabel 注册为按钮的 ActionListener
。我们可以这样做,因为 JButton 会创建动作事件,而 ChangeLabel 是一个 ActionListener(即实现了 ActionListener
接口)。
在上面的示例中要注意一件事。定义了两个类,一个用于在静态 main() 方法中包含程序代码,另一个用于定义程序中使用的类型。但是,没有理由不能将 main()
方法直接放在我们新类型的类中。事实上,将程序的 main()
方法放在组件的自定义子类中是 Swing 应用程序编程中常见的习惯用法。以这种方式重新组织后,程序如下所示
![]() |
合并后的类
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingConstants;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
// ActionEvent and ActionListener
// live in the java.awt.event
// package
// Application with a changing text field
public class ChangingLabel extends JLabel implements ActionListener {
// String constants which define the text that's displayed.
private static final String TEXT1 = "Hello, World!";
private static final String TEXT2 = "Hello again!";
// Instance field to keep track of the text currently
// displayed
private boolean usingText1;
// Constructor. Sets alignment and initial text, and
// sets the usingText1 field appropriately.
public ChangingLabel() {
this.setHorizontalAlignment(SwingConstants.CENTER);
this.setText(TEXT1);
usingText1 = true;
}
// A method to change the label text. If the
// current text is TEXT1, changes it to TEXT2,
// and vice-versa. The method is private here,
// because it is never called directly.
private void changeLabel() {
if(usingText1) {
this.setText(TEXT2);
usingText1 = false;
} else {
this.setText(TEXT1);
usingText1 = true;
}
}
// This method implements the ActionListener interface.
// Any time that an action is performed by a component
// that this object is registered with, this method
// is called. We can analyze the event object received
// here for more information if we want to, but it's not
// necessary in this application.
public void actionPerformed(ActionEvent e) {
this.changeLabel();
}
// Main method. This is the code that is executed when the
// program is run
public static void main(String[] args) {
// Constructs the ChangingLabel. All the
// intialization is done be the default
// constructor, defined in the ChangingLabel
// class below.
ChangingLabel changingLabel = new ChangingLabel();
// Create the button
JButton button = new JButton("Button");
// Register the ChangingLabel as an action
// listener to the button. Whenever the
// button is pressed, its ActionEvent will
// be sent the ChangingLabel's
// actionPerformed() method, and the code
// there will be executed.
button.addActionListener(changingLabel);
// Create the frame
JFrame frame = new JFrame("Hello");
// Add the label and the button to the
// frame, using layout constants.
frame.add(changingLabel, BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.toFront();
}
}
|
此程序执行的操作与第一个程序完全相同,但将所有代码组织到一个类中。
在动作、更改、鼠标和其他监听器中执行操作的线程称为Swing 线程。这些方法必须快速执行并返回,因为 Swing 线程必须可用于 Swing 框架以保持应用程序的响应性。换句话说,如果您执行一些耗时的计算或其他活动,您必须在单独的线程中执行,而不是在 Swing 线程中。但是,如果您从自己启动的非 Swing 线程访问 Swing 组件,它们可能会由于竞争条件而挂起,如果 Swing 线程同时对其进行操作。因此,必须通过中间 Runnable 使用 SwingUtilities.invokeAndWait()
方法从非 Swing 线程访问 Swing 组件
![]() |
多线程
String myMessage = "abc";
final String message = myMessage; // Making final allows to access from inner class here.
SwingUtilities.invokeAndWait(new Runnable {
public void run() {
myLabel.setText(message);
}
})
|
此调用将在适当的时候触发setText(message),此时 Swing 线程将准备好响应。当实现仅在缓慢过程本身在 Swing 线程之外实现时才能正常工作的移动进度条时,这种方法也很常见。