1. 程式人生 > >事件驅動程式設計學習筆記

事件驅動程式設計學習筆記

        如果希望可以編寫一個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。監聽器通過更新畫面來模擬動畫。