開発覚え書き

ソフトウェアシステムの開発に関わるさまざまなことの覚え書き

Swing ボタン部品のアクションイベントの処理 (標準Look & Feelのとき)

2006-06-13 10:36:23 | Weblog
昨日の部分は細部が間違っていたので修正した。

Swing部品の標準Look & Feelのときの処理 (ボタン部品のアクションイベントについて) は以下のようになっている。
JDK のソースをいくら見てもわからず、結局、JBuilder Personal 9 日本語版 と添付のJDK 1.4.1_02-b06 でイベント処理用メソッドにデバッガのブレークをかけて調べた結果である。
だだし、以下の文中で参照しているJDKのソースは 1.4.2_11-b06 のものである。

Swingのイベント処理はAWTの低レベルなイベント処理の上に構成されており
非常にややこしいが、何というか勉強になった気がする。

[処理の概要説明]

Swingの部品は多くが見かけ(view)と動作内容(model)を分離する形で記述されており、ボタンも内部に動作を規定するモデルを持っている。イベント処理にはモデルが密接に関っている。

ボタン構築時に

     javax.swing.plaf.basic.BasicButtonUI

クラスで、メソッドが
     public void installUI(JComponent c)
     protected void installListeners(AbstractButton b)
と呼び出され、そこで

     javax.swing.plaf.basic.BasicButtonListener

クラスのインスタンスが、ボタンの MouseListener, MouseMotionListener, FocusListener, ChangeListener, PropertyChangeListener として登録されているようだ。(メソッドinstallUI()呼び出しコード等は未確認)

ボタンクリックのマウスイベントは、 java.awt.Component の protected void processEvent(AWTEvent e) から protected void processMouseEvent(MouseEvent e) で処理されるが、その中でリスナとして

     javax.swing.plaf.basic.BasicButtonListener

クラスのインスタンスの該当メソッドが呼び出されることになる。


ボタンクリック時には、このリスナの

     mouseReleased()

から、ボタンの getModel() によって得られるモデル ButtonModel の setPressed(true) が呼び出される。

JButton の場合は、

     javax.swing.DefaultButtonModel の setPressed()

が呼ばれ、そこで

     javax.swing.DefaultButtonModel の protected void fireActionPerformed(ActionEvent e)

が呼ばれる。
(JCheckBox など JToggleButton の場合は、

     javax.swing.JToggleButton.ToggleButtonModel (内部staticクラス。DefaultButtonModelのサブクラス) の setPressed()

が呼ばれ、そこでトグルチェックをやった後

     javax.swing.DefaultButtonModel の protected void fireActionPerformed(ActionEvent e)

が呼ばれる。)


モデル (DefaultButtonModel や ToggleButtonModel 等のインスタンス) の fireActionPerformed() では、 インスタンス変数である

     protected EventListenerList listenerList (java.util.EventListener クラス)

を参照し、登録されている ActionListener を順次検索し、それぞれの

     public void ationPerformed(ActionEvent e)

を順次呼び出している。
ところで、ボタンのコンストラクタの中の
     setModel(new DefaultButtonModel());

     setModel(new ToggleButtonModel());
によって呼ばれる

     AbstractButton の public void setModel(ButtonModel newModel)

の中で、モデル (DefaultButtonModel や ToggleButtonModel 等のインスタンス) の ChangeListener, ActionListener, ItemListener として以下のようなリスナが登録されている。

登録コード


            changeListener = createChangeListener();
            actionListener = createActionListener();
            itemListener = createItemListener();
            newModel.addChangeListener(changeListener);
            newModel.addActionListener(actionListener);
            newModel.addItemListener(itemListener);


登録リスナクラス


    protected ChangeListener createChangeListener() {
        return (ChangeListener) new ButtonChangeListener();
    }
    protected class ButtonChangeListener implements ChangeListener, Serializable {
        ButtonChangeListener() {
        }

        public void stateChanged(ChangeEvent e) {
            Object source = e.getSource();

            updateMnemonicProperties();
            fireStateChanged();
            repaint();
        }
    }

    private class ForwardActionEvents implements ActionListener, Serializable {
        public void actionPerformed(ActionEvent event) {
            fireActionPerformed(event);
        }
    }
    protected ActionListener createActionListener() {
        return new ForwardActionEvents();
    }

    private class ForwardItemEvents implements ItemListener, Serializable {
        public void itemStateChanged(ItemEvent event) {
            fireItemStateChanged(event);
        }
    }
    protected ItemListener createItemListener() {
        return new ForwardItemEvents();
    }


アクションイベントではモデルのリスナは ForwardActionEvents であり、イベント処理はその

     public void actionPerformed(ActionEvent event)

にて行われる。その中味は

     fireActionPerformed(event);

であるため、結局

     AbstractButton の protected void fireActionPerformed(ActionEvent event)

メソッドに処理が移る。
ここで AbstractButton は JComponent のサブクラスなので、JComponent に用意されているインスタンス変数

     protected EventListenerList listenerList

を参照し、そこに登録されているアクションリスナの actionPerformed(ActionEvent e) を順次呼び出す。
JComponent に用意されているインスタンス変数 listenerList はボタンの addActionListener() 等で利用されているため、ここでようやく登録されたリスナのメソッドが呼び出されることになる。


[ボタンクリックからイベント処理用メソッドに至るまでのスタックトレース]

以下は (A) JButtonnクリック時 (B) JCheckBoxクリック時 のイベント処理用メソッドに至るまでのスタックトレースである。

(A) JButtonをクリックしたとき

処理用メソッド
jButton1_actionPerformed() : 104, フレームクラス, ソース.java

登録してあるリスナのイベントハンドラ
actionPerformed() : 177, アダプタクラス, ソース.java


fireActionPerformed() : 1764, javax.swing.AbstractButton, AbstractButton.java
actionPerformed() : 1817, javax.swing.AbstractButton$ForwardActionEvents, AbstractButton.java
fireActionPerformed() : 419, javax.swing.DefaultButtonModel, DefaultButtonModel.java
setPressed() : 257, javax.swing.DefaultButtonModel, DefaultButtonModel.java
mouseReleased() : 245, javax.swing.plaf.basic.BasicButtonListener, BasicButtonListener.java
processMouseEvent() : 5134, java.awt.Component, Component.java
processEvent() : 4931, java.awt.Component, Component.java
processEvent() : 1566, java.awt.Container, Container.java
dispatchEventImpl() : 3639, java.awt.Component, Component.java
dispatchEventImpl() : 1623, java.awt.Container, Container.java
dispatchEvent() : 3480, java.awt.Component, Component.java
retargetMouseEvent() : 3450, java.awt.LightweightDispatcher, Container.java
processMouseEvent() : 3165, java.awt.LightweightDispatcher, Container.java
dispatchEvent() : 3095, java.awt.LightweightDispatcher, Container.java
dispatchEventImpl() : 1609, java.awt.Container, Container.java
dispatchEventImpl() : 1590, java.awt.Window, Window.java
dispatchEvent() : 3480, java.awt.Component, Component.java
dispatchEvent() : 450, java.awt.EventQueue, EventQueue.java
pumpOneEventForHierarchy() : 197, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEventsForHierarchy() : 150, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 144, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 136, java.awt.EventDispatchThread, EventDispatchThread.java
run() : 99, java.awt.EventDispatchThread, EventDispatchThread.java



(B) JCheckBox (JToggleButtonの一種) をクリックしたとき

処理用メソッド
jCheckBox1_actionPerformed() : 156, フレームクラス, ソース.java

登録してあるリスナのイベントハンドラ
actionPerformed() : 216, アダプタクラス, ソース.java


fireActionPerformed() : 1764, javax.swing.AbstractButton, AbstractButton.java
actionPerformed() : 1817, javax.swing.AbstractButton$ForwardActionEvents, AbstractButton.java
fireActionPerformed() : 419, javax.swing.DefaultButtonModel, DefaultButtonModel.java
setPressed() : 273, javax.swing.JToggleButton$ToggleButtonModel, JToggleButton.java
mouseReleased() : 245, javax.swing.plaf.basic.BasicButtonListener, BasicButtonListener.java
processMouseEvent() : 5134, java.awt.Component, Component.java
processEvent() : 4931, java.awt.Component, Component.java
processEvent() : 1566, java.awt.Container, Container.java
dispatchEventImpl() : 3639, java.awt.Component, Component.java
dispatchEventImpl() : 1623, java.awt.Container, Container.java
dispatchEvent() : 3480, java.awt.Component, Component.java
retargetMouseEvent() : 3450, java.awt.LightweightDispatcher, Container.java
processMouseEvent() : 3165, java.awt.LightweightDispatcher, Container.java
dispatchEvent() : 3095, java.awt.LightweightDispatcher, Container.java
dispatchEventImpl() : 1609, java.awt.Container, Container.java
dispatchEventImpl() : 1590, java.awt.Window, Window.java
dispatchEvent() : 3480, java.awt.Component, Component.java
dispatchEvent() : 450, java.awt.EventQueue, EventQueue.java
pumpOneEventForHierarchy() : 197, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEventsForHierarchy() : 150, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 144, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 136, java.awt.EventDispatchThread, EventDispatchThread.java
run() : 99, java.awt.EventDispatchThread, EventDispatchThread.java

(間違い) Swing ボタン部品のアクションイベントの処理 (標準Look & Feelのとき)

2006-06-12 16:20:56 | Weblog
Swing部品の標準Look & Feelのときの処理 (ボタン部品のアクションイベントについて) は以下のようになっている。
JDK のソースをいくら見てもわからず、結局、JBuilder Personal 9 日本語版 と添付のJDK 1.4.1_02-b06 でイベント処理用メソッドにデバッガのブレークをかけて調べた結果である。
非常にややこしいが、何というか勉強になった気がする。

[処理の概要説明]

ボタン構築時に

     javax.swing.plaf.basic.BasicButtonUI

クラスで、

     javax.swing.plaf.basic.BasicButtonListener

クラスのインスタンスが、ボタンの MouseListener, addMouseMotionListener, FocusListener, ChangeListener, PropertyChangeListener として登録されている。

ボタンクリックのマウスイベントは、 java.awt.Component の protected void processEvent(AWTEvent e) から protected void processMouseEvent(MouseEvent e) で処理されるが、その中でリスナとして

     javax.swing.plaf.basic.BasicButtonListener

クラスのインスタンスの該当メソッドが呼び出されることになる。


ボタンクリック時には、このリスナの

     mouseReleased()

から、ボタンの getModel() によって得られる ButtonModel の setPressed(true) が呼び出される。

JButton の場合は、

     javax.swing.DefaultButtonModel の setPressed()

が呼ばれ、そこで

     javax.swing.DefaultButtonModel の protected void fireActionPerformed(ActionEvent e)

が呼ばれる。
(JCheckBox など JToggleButton の場合は、

     javax.swing.JToggleButton.ToggleButtonModel (内部staticクラス。DefaultButtonModelのサブクラス) の setPressed()

が呼ばれ、そこでトグルチェックをやった後

     javax.swing.DefaultButtonModel の protected void fireActionPerformed(ActionEvent e)

が呼ばれる。)


DefaultButtonModel の fireActionPerformed() では、 インスタンス変数である

     protected EventListenerList listenerList (java.util.EventListener クラス)

を参照し、登録されている ActionListener を順次検索し、それぞれの

     public void ationPerformed(ActionEvent e)

を順次呼び出している。

※ なお、JButton や JCheckBox などのボタンでは DefaultButtonModel の listenerList がイベント発火のときに使用され、JComponent に用意されているインスタンス変数

protected EventListenerList listenerList

は使用していないようだ。
AbstractButton の addActionListener 等のコードでは JComponent の listenerList を利用しているのだが...
不思議だ。



[ボタンクリックからイベント処理用メソッドに至るまでのスタックトレース]

以下は (A) JButtonnクリック時 (B) JCheckBoxクリック時 のイベント処理用メソッドに至るまでのスタックトレースである。

(A) JButtonをクリックしたとき

処理用メソッド
jButton1_actionPerformed() : 104, フレームクラス, ソース.java

登録してあるリスナのイベントハンドラ
actionPerformed() : 177, アダプタクラス, ソース.java


fireActionPerformed() : 1764, javax.swing.AbstractButton, AbstractButton.java
actionPerformed() : 1817, javax.swing.AbstractButton$ForwardActionEvents, AbstractButton.java
fireActionPerformed() : 419, javax.swing.DefaultButtonModel, DefaultButtonModel.java
setPressed() : 257, javax.swing.DefaultButtonModel, DefaultButtonModel.java
mouseReleased() : 245, javax.swing.plaf.basic.BasicButtonListener, BasicButtonListener.java
processMouseEvent() : 5134, java.awt.Component, Component.java
processEvent() : 4931, java.awt.Component, Component.java
processEvent() : 1566, java.awt.Container, Container.java
dispatchEventImpl() : 3639, java.awt.Component, Component.java
dispatchEventImpl() : 1623, java.awt.Container, Container.java
dispatchEvent() : 3480, java.awt.Component, Component.java
retargetMouseEvent() : 3450, java.awt.LightweightDispatcher, Container.java
processMouseEvent() : 3165, java.awt.LightweightDispatcher, Container.java
dispatchEvent() : 3095, java.awt.LightweightDispatcher, Container.java
dispatchEventImpl() : 1609, java.awt.Container, Container.java
dispatchEventImpl() : 1590, java.awt.Window, Window.java
dispatchEvent() : 3480, java.awt.Component, Component.java
dispatchEvent() : 450, java.awt.EventQueue, EventQueue.java
pumpOneEventForHierarchy() : 197, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEventsForHierarchy() : 150, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 144, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 136, java.awt.EventDispatchThread, EventDispatchThread.java
run() : 99, java.awt.EventDispatchThread, EventDispatchThread.java


(B) JCheckBox (JToggleButtonの一種) をクリックしたとき

処理用メソッド
jCheckBox1_actionPerformed() : 156, フレームクラス, ソース.java

登録してあるリスナのイベントハンドラ
actionPerformed() : 216, アダプタクラス, ソース.java

fireActionPerformed() : 1764, javax.swing.AbstractButton, AbstractButton.java
actionPerformed() : 1817, javax.swing.AbstractButton$ForwardActionEvents, AbstractButton.java
fireActionPerformed() : 419, javax.swing.DefaultButtonModel, DefaultButtonModel.java
setPressed() : 273, javax.swing.JToggleButton$ToggleButtonModel, JToggleButton.java
mouseReleased() : 245, javax.swing.plaf.basic.BasicButtonListener, BasicButtonListener.java
processMouseEvent() : 5134, java.awt.Component, Component.java
processEvent() : 4931, java.awt.Component, Component.java
processEvent() : 1566, java.awt.Container, Container.java
dispatchEventImpl() : 3639, java.awt.Component, Component.java
dispatchEventImpl() : 1623, java.awt.Container, Container.java
dispatchEvent() : 3480, java.awt.Component, Component.java
retargetMouseEvent() : 3450, java.awt.LightweightDispatcher, Container.java
processMouseEvent() : 3165, java.awt.LightweightDispatcher, Container.java
dispatchEvent() : 3095, java.awt.LightweightDispatcher, Container.java
dispatchEventImpl() : 1609, java.awt.Container, Container.java
dispatchEventImpl() : 1590, java.awt.Window, Window.java
dispatchEvent() : 3480, java.awt.Component, Component.java
dispatchEvent() : 450, java.awt.EventQueue, EventQueue.java
pumpOneEventForHierarchy() : 197, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEventsForHierarchy() : 150, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 144, java.awt.EventDispatchThread, EventDispatchThread.java
pumpEvents() : 136, java.awt.EventDispatchThread, EventDispatchThread.java
run() : 99, java.awt.EventDispatchThread, EventDispatchThread.java