AWT/Swing——事件處理

前言

當用戶點擊圖形界面上的一個按鈕或者其餘Component時要有所響應,這纔是實現了圖形界面的交互功能。如何作出這些響應咱們就須要瞭解事件的處理機制。下面將分爲如下內容介紹AWT(Swing)中事件處理機制:java

什麼是事件

通俗一點來講就是某種狀態的改變,在咱們的圖形界面中就表現爲某個按鈕被點擊了,窗口被關閉了等。編程

什麼是事件處理

當某個事件發生時(界面中的某個Component的某個狀態發生改變時),咱們但願在這個時機執行一些代碼來作咱們但願作的事,這個就是事件處理。如點擊窗口關閉按鈕時,彈出對話框詢問用戶是否保存當前已經修改過的內容。
Java是面向對象的編程語言,Java中使用監聽器類來探測一個事件(改變),使用監聽器類中的方法來在事件發生的時候處理事件設計模式

事件處理中的三要素

事件源:是這個對象的狀態改變引起的事件,事件源一般是Component。編程語言

事件:事件源發生的狀態改變。如按鈕被鼠標左擊或者被鼠標右擊等。ide

事件監聽器:監聽器被安裝在某個Component上,負責監聽這個Component具體狀態被改變了。函數

AWT中事件處理的流程

外部誘使事件源狀態發生變化,產生事件對象,而後事件監聽器監聽到該事件的發生,作出響應。測試

  1. 首先將事件監聽器註冊到事件源上面
  2. 觸發事件源上的事件(改變狀態)
  3. 生成事件對象
  4. 事件監聽器監聽到該事件的發生,生成的事件對象當作參數傳入事件處理器(監聽器類中的方法)
  5. 調用事件處理器作出響應

舉例點擊事件

鼠標點擊按鈕後,文本框中顯示一行‘按鈕被點擊了'。
this

先是在按鈕上註冊了事件監聽器,監聽器中設置鼠標被點擊時應該調用的事件處理器是怎麼處理的;而後,鼠標點擊按鈕;生成按鈕被點擊的事件對象;事件監聽器監聽到點擊事件發生,就會傳入事件對象到事件處理器;最後,調用事件處理器中作出但願的響應。設計

public class TestEvent {
    public static void main(String[] args) {
        JFrame myFrame = new JFrame();
        JButton btn = new JButton("點擊我");
        JTextField field = new JTextField();
        //爲field指定寬高
        field.setPreferredSize(new Dimension(100, 40));
        //使用Jpanel容器裝載JButton和JTextFiedl組件
        JPanel jPanel = new JPanel();
        jPanel.add(btn);
        jPanel.add(field);
        myFrame.add(jPanel);
        //設置窗口的大小寬高都爲300像素以及位置距離屏幕左上方向右300像素以及向下300像素
        myFrame.setBounds(300, 300, 300, 300);
        //必須設置這個屬性 才能夠看見窗口
        myFrame.setVisible(true);
        
        //爲btn設置事件監聽器 監聽器採用匿名內部類的形式
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                field.setText("Button被點擊了");
            }
        });
    }
}

運行結果圖1
運行結果圖2

AWT中事件處理的模板

class XXXXListener implements XXXListener {
    @Override 
    public void xxx(ActionEvent e) {
        System.out.println("用戶單擊了按鈕");
    }
}
…
yyy.addXXXListener(new XXXXListener());

建立一個監聽器類,該類實現監聽器接口XXXListener,而後實現監聽器方法使得能夠處理對應事件3d

  • 在感興趣的Component上使用addXXXListener(...),添加監聽器即傳入監聽器的實例

  • 注意:

    ​ 不一樣的Component有不一樣的事件發生,可是通常添加監聽器的方法都是addXXXListener(...)。Component會有不一樣的事件發生,因此須要對感興趣的事件添加對應的監聽器。有些監聽器內部會有多個方法,能夠監聽多種事件,可是這些事件通常都相關。

    在點擊事件舉例中,咱們添加監聽器採用的匿名內部類的形式,能夠看出監聽器的實現形式仍是有多種。

監聽器(EventListener)的實現形式

監聽器是一種特殊的Java類。在AWT中,監聽器主要有如下幾種實現方式:

監聽器做爲外部類

規範易於理解、類自己能夠重用;不足在於通常狀況下不利於實現事件處理中的功能,由於不易於訪問界面中的屬性和方法

class BtnListener implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e) {
        //使用getSource獲取事件源對象 可是不易訪問到其餘組件
        JButton btn = (JButton)e.getSource();
        btn.setText("我被點擊了");
    }
}

public class TestListener {
    private JFrame myFrame = new JFrame("測試外部監聽器");
    private JButton btn = new JButton("點擊我");
    private JTextField field = new JTextField();
    
    //構造函數
    public TestListener() {
        init();
    }
    
    //初始化 爲按鈕添加事件監聽器
    public void init() {
        JPanel jPanel = new JPanel();
        jPanel.add(btn);
        field.setPreferredSize(new Dimension(100, 40));
        jPanel.add(field);
        
        //以建立外部類對象方式添加事件監聽器
        btn.addActionListener(new BtnListener());
        
        myFrame.add(jPanel);
        myFrame.setBounds(300, 300, 500, 300);
        //使得按鈕窗口的關閉按鈕能夠關閉窗口
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    //顯示窗口
    public void showFrame() {
        myFrame.setVisible(true);
    }
    
    public static void main(String[] args) {
        new TestListener().showFrame();
    }
}

監聽器做爲內部類

能夠方便的訪問主類中的任何屬性和方法,包括私有方法;不足在於使得主類過於複雜、不能夠在不一樣界面中重用

public class TestListener {
    //內部類
    class BtnListener implements ActionListener{
        @Override
        public void actionPerformed(ActionEvent e) {
            //能夠方便操做主類的其餘屬性
            field.setText("Btn被點擊了");
        }
    }
    ...
    //初始化 爲按鈕添加事件監聽器
    public void init() {
        ...
        //以建立內部類對象方式添加事件監聽器
        btn.addActionListener(new BtnListener());
        ...
    }
    ...
}

監聽器做爲主類自己

能夠方便地訪問本類中的任何方法和屬性;不足在於使得本類的方法過多

//主類做爲監聽器
public class TestListener implements ActionListener{
    private JFrame myFrame = new JFrame("測試主類做爲監聽器");
    private JButton btn = new JButton("點擊我");
    private JTextField field = new JTextField();
    
    //構造函數
    public TestListener() {
        init();
    }
    
    //實現的處理方法
    @Override
    public void actionPerformed(ActionEvent e) {
        field.setText("Btn被點擊了");
    }
    
    //初始化 爲按鈕添加事件監聽器
    public void init() {
        JPanel jPanel = new JPanel();
        jPanel.add(btn);
        field.setPreferredSize(new Dimension(100, 40));
        jPanel.add(field);
        
        //主類自己做爲監聽器對象傳入
        btn.addActionListener(this);
        
        myFrame.add(jPanel);
        myFrame.setBounds(300, 300, 500, 300);
        //使得按鈕窗口的關閉按鈕能夠關閉窗口
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    //顯示窗口
    public void showFrame() {
        myFrame.setVisible(true);
    }
    
    public static void main(String[] args) {
        new TestListener().showFrame();
    }
}

監聽器做爲匿名內部類

能夠方便地訪問主類的方法和屬性;不足在於對於每一個事件都須要寫匿名內部類,不能重用、不利於理解

//主類做爲監聽器
public class TestListener{
    private JFrame myFrame = new JFrame("測試主類做爲監聽器");
    private JButton btn = new JButton("點擊我");
    private JTextField field = new JTextField();
    
    //構造函數
    public TestListener() {
        init();
    }
    
    //初始化 爲按鈕添加事件監聽器
    public void init() {
        JPanel jPanel = new JPanel();
        jPanel.add(btn);
        field.setPreferredSize(new Dimension(100, 40));
        jPanel.add(field);
        
        //以匿名內部類的方式建立監聽器
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                field.setText("Btn被點擊了");
            }
        });
        
        //或者使用lambda表達式
//      btn.addActionListener(event->field.setText("Btn被點擊了"));
        
        myFrame.add(jPanel);
        myFrame.setBounds(300, 300, 500, 300);
        //使得按鈕窗口的關閉按鈕能夠關閉窗口
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    //顯示窗口
    public void showFrame() {
        myFrame.setVisible(true);
    }
    
    public static void main(String[] args) {
        new TestListener().showFrame();
    }
}

關於使用lambda表達式的理解:若某個方法使用的類是「衆所周知「的而且類中的方法是惟一的,咱們就只須要給出參數和一些執行代碼代替原來複雜的一堆代碼。由於惟一性,編譯器就會推斷出使用的是什麼類以及類中的方法。
上面咱們可使用lamnda表達式event->field.setText(...)代替了匿名內部類那麼多代碼,那是由於編譯器能夠推斷出addListener(...)須要傳入的參數是實現了ActionListener接口的類,這個類中有惟一的方法就是actionPerformed以及這個方法只有一個參數並且也知道其參數類型。

使用lambda表達式雖然能夠簡化咱們的代碼,可是咱們須要理清楚其本來應該使用什麼類以及什麼方法。

如果省略的方法由多個參數則->左邊的參數須要使用()括起來如(a,b,c)->後有多行須要執行的代碼則使用{}將代碼括起來。

事件適配器(EventAdapter)

咱們在建立監聽器類時須要實現其實現的相應監聽器接口中的全部方法。前面提到過,某些監聽器接口內會有多個方法,可是咱們只對一些方法感興趣,有沒有方法只用實現感興趣的方法其餘方法就不實現呢。答案是有的,那就是使用事件適配器。Java內部已經幫咱們事先寫了一些適配器,咱們只須要實現感興趣的適配器,而後重寫咱們感興趣的方法。

如果沒有適配器,咱們想監聽鼠標點擊事件,咱們先看MouseListener接口的源碼實現:

public interface MouseListener extends EventListener {

    /**
     * Invoked when the mouse button has been clicked (pressed
     * and released) on a component.
     */
    public void mouseClicked(MouseEvent e);

    /**
     * Invoked when a mouse button has been pressed on a component.
     */
    public void mousePressed(MouseEvent e);

    /**
     * Invoked when a mouse button has been released on a component.
     */
    public void mouseReleased(MouseEvent e);

    /**
     * Invoked when the mouse enters a component.
     */
    public void mouseEntered(MouseEvent e);

    /**
     * Invoked when the mouse exits a component.
     */
    public void mouseExited(MouseEvent e);
}

咱們須要實現mouseClicked方法之外的其餘4個方法,能夠說是很是麻煩的。咱們再看MouseAdapter的源碼實現:

public abstract class MouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener {
    /**
     * {@inheritDoc}
     */
    public void mouseClicked(MouseEvent e) {}

    /**
     * {@inheritDoc}
     */
    public void mousePressed(MouseEvent e) {}

    /**
     * {@inheritDoc}
     */
    public void mouseReleased(MouseEvent e) {}

    /**
     * {@inheritDoc}
     */
    public void mouseEntered(MouseEvent e) {}

    /**
     * {@inheritDoc}
     */
    public void mouseExited(MouseEvent e) {}

    /**
     * {@inheritDoc}
     * @since 1.6
     */
    public void mouseWheelMoved(MouseWheelEvent e){}

    /**
     * {@inheritDoc}
     * @since 1.6
     */
    public void mouseDragged(MouseEvent e){}

    /**
     * {@inheritDoc}
     * @since 1.6
     */
    public void mouseMoved(MouseEvent e){}
}

能夠看出適配器的實現很是巧妙,爲每一個方法添加一個空的方法體而爲咱們進行一次封裝,咱們只需重寫咱們感興趣的方法就可,其餘方法也不會受到影響。

AWT中的事件分類

AWT中的事件主要分爲兩大類:低級事件和高級事件

低級事件

低級事件就比較底層(比較細節),主要有:

  • ComponentEvent:組件事件,當組件尺寸發生改變、位置發生變化、顯示/隱藏狀態發生改變時,就會觸發該事件

  • ContainerEvent:容器事件,當容器裏增長、刪除組件時,就會觸發該事件

  • WindowEvent:窗口事件,當窗口狀態發生改變(打開、關閉、最大化、最小化)時,就會觸發該事件

  • FocusEvent:焦點事件,當組件獲得焦點或者失去焦點時,就會觸發該事件

  • KeyEvent:鍵盤事件,當鍵盤按鍵被按下、鬆開時就會觸發該事件

  • MouseEvent:鼠標事件,當一個組件被鼠標按下、放開、在其上面移動鼠標時,就會觸發該事件

  • PaintEvent:繪製事件,當GUI組件調用update()/paint()方法時觸發該事件,該事件並不是專用於事件處理模型中,通常也不多直接監聽該事件

高級事件

高級事件基於語義,並不和特定的動做相關,而依賴於觸發該事件的組件類別。主要分爲:

  • ActionEvent:動做事件,當按鈕、菜單等能產生Action的項目被單擊,或者在TextField中按下Enter按鈕時,就會觸發該事件
  • AdjustmentEvent:調節事件,在滑動條上移動滑塊調節數值時觸發該事件
  • ItemEvent:–選項事件,當在有不少項目的組件中,選中或者取消選中了某一個項目,就會觸發該事件
  • TextEvent:文本事件,當文本框或者文本域這類具備文本的組件中,文本發生變化時,就會觸發該事件

小結

總結了關於事件處理的大部分基礎,基礎概念有了,重要的仍是多寫代碼實踐。另外事件處理中仍是涉及到不少技巧,好比使用lambda表達式、使用設計模式的適配器思想去簡化設置監聽器的代碼。在本身寫的過程當中纔會體會更多

相關文章
相關標籤/搜索