事件驅動程式設計學習筆記
如果希望可以編寫一個GUI程式提示使用者輸入貸款總額,年利率,年數,然後點選OK按鈕獲取月償還額和總償還額。則必須使用時間驅動程式設計來編寫程式碼。
事件和事件源
能建立一個事件並能觸發該事件的元件成為源物件或是源元件。如按鈕是按鈕點選動作事件的源物件。一個事件是一個事件類的例項化。事件的根類是java.util.EventObject。
除了ListSelectionEvent和ChangeEvent之外所有的事件類都包括在java.awt.event包中,ListSelectionEvent和ChangeEvent在java.swing.event包中。AWT事件本來就是為了AWT元件設計的,但是很多Swing元件都會用到它們。
監聽器,註冊,處理事件
Java使用一種基於委託的模型來處理事件:源物件觸發一個事件,對此事件感興趣的物件會處理它。對此事件感興趣的物件成為監聽器(listener)。一個物件要成為源物件的監聽器需要具備兩個條件:
1. 監聽器物件的類必須是相應的事件監聽器介面的例項,以確保監聽器有處理這個時間的正確方法。
2. 監聽器物件必須有源物件註冊。
現在我們可以編寫一個程式,使用兩個按鈕控制一個圓的大小。程式碼如下
import java.util.*; import javax.swing.*; import javax.swing.border.Border; import java.io.*; import java.awt.*; import java.awt.event.*; class ArcPanel extends JPanel { private int radius = 5; protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, 2 * radius, 2 * radius); } public void enlarge() { radius += 5; repaint(); } public void shrink() { radius -= 5; repaint(); } } public class Main extends JFrame { private JButton jbtEnlarge = new JButton("Enlarge"); private JButton jbtShrink = new JButton("Shrink"); private ArcPanel canvas = new ArcPanel(); public Main() { JPanel panel = new JPanel(); panel.add(jbtEnlarge); panel.add(jbtShrink); this.add(canvas, BorderLayout.CENTER); this.add(panel, BorderLayout.SOUTH); jbtEnlarge.addActionListener(new EnlargeListener()); jbtShrink.addActionListener(new ShrinkListener()); } class EnlargeListener implements ActionListener { public void actionPerformed(ActionEvent e) { canvas.enlarge(); } } class ShrinkListener implements ActionListener { public void actionPerformed(ActionEvent e) { canvas.shrink(); } } public static void main(String[] args) { Main frame = new Main(); frame.setTitle("Main"); frame.setSize(250, 300); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
由上面的程式碼顯示,我將EnlargeListener和ShrinListener定義成了內部類,通常,如果一個類只是被外部類使用,就將該類定義為內部類。一個內部類有如下特徵:
1. 一個內部類被編譯成一個名為OutClass$InnerClass.class的類。
2. 內部類可以引用定義在它巢狀外部類中的資料和方法,所以,不需要將外部類物件的引用傳遞給內部類的構造方法,因此,內部類可以使程式更加簡單和簡潔。
3. 使用可見修飾符定義內部類時,遵從應用與在類成員上一樣的可見性原則。
4. 可以講內部類定義為static。一個static內部類可以使用外部類的名字訪問。一個static類是不能訪問外部類的非靜態成員的。
5. 內部類物件經常在外部類中建立。但是也可以從另一個類中建立一個內部類的物件,如果該內部類是非靜態的,必須先建立一個外部類的例項,然後使用下面的語法建立以一個內部類物件:
OutClass.InnerClassinnerObject = outObject.new InnerClass();
6. 如果內部類是靜態的,那麼使用下面的語法為它建立一個物件:
OutClass.InnerClassinnObject = new OutClass.InnerClass();
匿名內部類:
我們可以把上面的監聽器重新寫,程式碼如下:
jbtEnlarge.addActionListener(new ActionListener()
{
public void actionPerformed(ActionListen e)
{
canvas.enlarge();
}
})
匿名內部類:
1. 匿名內部類必須總是擴充套件父類或實現介面,但是它不能有顯示的extends或者implements子句。
2. 匿名類必須實現父類或者介面內的所有方法。
3. 匿名內部類總是使用它的父類的無參構造方法來建立例項,如果匿名內部類實現了介面,構造方法就是Object();
4. 匿名內部類被編譯為一個名為OuterClassName$n.class的類。
定義監聽器的另外一種方式
話不多說,貼上程式碼:
import java.util.*;
import javax.swing.*;
import javax.swing.border.Border;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JFrame {
private JButton jbtEnlarge = new JButton("Enlarge");
private JButton jbtShrink = new JButton("Shrink");
private ArcPanel canvas = new ArcPanel();
private ButtonListener listener = new ButtonListener();
public Main() {
JPanel panel = new JPanel();
panel.add(jbtEnlarge);
panel.add(jbtShrink);
this.add(canvas, BorderLayout.CENTER);
this.add(panel, BorderLayout.SOUTH);
jbtEnlarge.addActionListener(listener);
jbtShrink.addActionListener(listener);
}
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == jbtEnlarge)
canvas.enlarge();
else if(e.getSource() == jbtShrink)
canvas.shrink();
}
}
class ArcPanel extends JPanel {
private int radius = 5;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, 2 * radius, 2 * radius);
}
public void enlarge() {
radius += 5;
repaint();
}
public void shrink() {
radius -= 5;
repaint();
}
}
public static void main(String[] args) {
Main frame = new Main();
frame.setTitle("Main");
frame.setSize(250, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
如上程式碼所示,我們只定義了一個監聽器 listener。我們可以通過getSource()方法獲取我們滑鼠點選的按鈕,這樣可以減少類的定義從而簡化程式碼。
例項:貸款計算器
要求:
(1) 建立一個如圖所示的使用者介面
(2) 建立一個5行2列的GridLayout面板,新增文字和標籤
(3) 建立一個FlowLayout面板,並新增一個按鈕
(4) 將兩個面板新增到框架中
(5) 處理事件
程式碼如下:
import java.util.*;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JFrame {
private JTextField jtfAnnualInterestRate = new JTextField("please type rate here");
private JTextField jtfNumberOfYears = new JTextField("please type year here");
private JTextField jtfLoanAmount = new JTextField("please type loanAmount here");
private JTextField jtfMonthlyPayment = new JTextField("output");
private JTextField jtfTotalPayment = new JTextField("output");
private JButton jbtComputeLoan = new JButton("Compute Payment");
public class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e) {
double interest = Double.parseDouble(jtfAnnualInterestRate.getText());
int year = Integer.parseInt(jtfNumberOfYears.getText());
double loanAmount = Double.parseDouble(jtfLoanAmount.getText());
// loan(interest,year,loanAmount);
jtfMonthlyPayment.setText(String.format("%.2f", loanAmount));//這裡只是一個替代。。。。。
jtfTotalPayment.setText(String.format("%.2f", loanAmount));// 這裡只是一個替代。。。。。
}
}
public Main()
{
JPanel p1 = new JPanel(new GridLayout(5,2));
p1.add(new JLabel("Annual Interest Rate"));
p1.add(jtfAnnualInterestRate);
p1.add(new JLabel("Number of Years"));
p1.add(jtfNumberOfYears);
p1.add(new JLabel("Loan Amount"));
p1.add(jtfLoanAmount);
p1.add(new JLabel("Monthly Payment"));
p1.add(jtfMonthlyPayment);
p1.add(new JLabel("Total Payment"));
p1.add(jtfTotalPayment);
p1.setBorder(new TitledBorder("Enter loan amount,interest rate,and year"));
JPanel p2 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p2.add(jbtComputeLoan);
add(p1,BorderLayout.CENTER);
add(p2,BorderLayout.SOUTH);
jbtComputeLoan.addActionListener(new ButtonListener());
}
public static void main(String[] args) {
Main frame = new Main();
frame.pack();
frame.setTitle("LoanCalculator");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Loan類的實現我已經實現過了,可是檔案丟失了,然後我又不想再寫一遍。。。。。
例項學習:使用滑鼠在面板上拖動訊息(用到方便介面卡)
上程式碼:
import java.util.*;
import java.util.Timer;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JFrame {
static class MovableMessagePanel extends JPanel {
private String s = "Welcomea To Java!";
private int x = 20;
private int y = 20;
public MovableMessagePanel(String s) {
this.s = s;
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
});
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString(s, x, y);
}
}
public Main() {
MovableMessagePanel p =new MovableMessagePanel("Hello Java!");
setLayout(new BorderLayout());
add(p);
}
public static void main(String[] args) {
Main frame = new Main();
frame.setTitle("MoveMessageDemo");
frame.setSize(200,100);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
使用MouseMotionAdapter覆蓋mouseDragged方法,如果內部類實現了MouseMotionListener介面,即使監聽器並不關心某些事件,也必須要實現所有的處理器。
本章小結:
1. 事件類的根類是java.util.EventObject。EventObject的子類處理各種特殊型別的時間,例如動作事件,視窗時間,元件事件,滑鼠事件和按鍵事件。可以使用EventObject類中的getSource()例項方法判斷事件的源物件。如果一個元件能夠觸發整個事件,那麼它的所有子類都能觸發同類型的事件。
2. 監聽器物件的類必須時間相應的時間監聽器介面。Java語言為每種事件類提供監聽器介面。XEvent的監聽器介面通常命名為XListener,但是MouseMotionListener除外。例如,ActionEvent對應的監聽器介面是ActionListener,每個ActionEvent的監聽器都應該實現ActionListener介面,監聽器介面包含成為處理器的處理事件的方法。
3. 監聽器物件必須由源物件註冊。註冊方法依賴於事件的型別。對ActionEvent來講,註冊方法是addActionListener。一般來說XEvent的方法命名為addXListener。
4. 內部類或者是巢狀類是定義在另一個類中的類。內部類可以應用定義在它巢狀的外部類中的資料和方法。所以,不需要將外部類的引用傳遞給內部類的構造方法。
5. 方便介面卡能夠提供監聽器介面中所有方法的預設實現類的支援類。Java為每一個AWT監聽器介面提供多個處理器的方便監聽器介面卡,XListenerde 方便監聽器介面卡命名為Xadapter。。
6. 一個源物件可以觸發幾種型別的事件。對每種事件,源物件維護一個註冊的監聽器列表,通過呼叫監聽器物件的處理器,通知所有已經註冊的監聽器取處理事件。
7. 在一個元件上點選,釋放,移動或拖動滑鼠就會觸發滑鼠事件。滑鼠事件物件捕獲事件,例如和事件相關的點選次數和滑鼠點的位置(x,y座標)
8. Java提供兩個處理滑鼠事件的監聽器介面,MouseListener和MouseMotionListner來處理事件,實現MouseListener介面來監聽注入按下,釋放,點選,輸入或退出滑鼠等動作,實現MouseMotionListener介面來監聽注入移動或拖動滑鼠的動作。
9. KeyEvent物件描述事件的性質(即按下,釋放或敲擊一個鍵)以及相對應的鍵值
10. 當按下按鍵是就會呼叫keyPressed處理器,當釋放按鍵時會呼叫keyReleased處理器,而當敲入一個統一碼字元鍵是,就會呼叫keyTyped處理器。如果某個按鍵沒有統一碼(如功能鍵,修改鍵,動作鍵和控制鍵),則不會呼叫keyTyped處理器。
可以使用Timer類控制Java的動畫。定時器以固定頻率觸發ActionEvent。監聽器通過更新畫面來模擬動畫。