響應式 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 執行緒之外實現時才能正常工作的移動進度條時,這種方法也很常見。
