Swing Application Framework 簡單介紹(四)

02:59下午 二月 03, 2009 in category Swing by UndeadJ

Actions
    
Swing Application Framework 打算利用Anntatin - @Action來取代ActionListener中的actionPerformed(ActionEvent e)方法。
    
在這之前,如果我們要為元件增加ActionListener,不是實作ActionListener()中的actionPerformed(ActionEvent e)方法,就是利用Action/ActionMap。
    
但是不論那一種,使用上還是不太方便。
    
一、方法名稱的問題:

動作事件(ActionListener)這個方法名稱不夠直覺,不能夠清楚的表達元件要做的事
必須再到實作事件的地方,才能清楚某元件(準備)要做什麼事。

button.addActionListener() // <- 不能夠直接反應(說明)button要做的事

二、EDT的問題:     

在處理事件的過程式,還必須小心是否違反EDT的原則,或者在程式碼內穿插一堆檢查的機制。

  if ( SwingUtilities.isEventDispatchThread() ) {
      //DoSomeThings   
  } else {
      SwingUtilities.invokeLater(new Runnable(){
        public void run(){
        //DoSomeThings
        }
      });
  })

三、共用事件的問題: 

當二個以上的元件都註冊監聽相同的動作事件時,想要針對不同元件做不同的事時,

不管是利用Action 或是getSource() 或是 setActionCommand()...等等,最後還是要經過判斷,才能對各別元件做特定的事情。

這樣一來,當共用事件的元件一多,程式中會添加太多的if-else。
        
若是為元件們建立自訂的ActionListene or Action,程式會因為這些變得很臭長,且不好維護。
        
上述這二種方式,都不是很好的作法。


  //if-else...Orz
        
        button1.addActionListener(new MyActionListener());
        button2.addActionListener(new MyActionListener());
                                        .
                                        .
                                        .
        buttonN.addActionListener(new MyActionListener());
        
        if (e.getSource()==button1){
        
        }else if(e.getSource()==button1){
            .
            .
            .        
        }else if(e.getSource()==buttonN){
        
        }

來試看看@Action,你會愛上她 :)   
    
使用的方法很簡單~ ( 記得! 事件動作也是經由ApplicationContext來管理的 )

首先利用Application中的getContext()來取得ApplicationContext實體

ApplicationContext ctxt = Application.getInstance().getContext()

 


再利用ApplicationContext實體來利得相對應的ActionMap實體
        
 ActionMap map = getContext().getActionMap();    

當getActionMap()一被呼叫,它會幫我們做好下面這些事: 

1. 利用ResourceMap.properties檔案取得相對應的key值,做為Action欄位的值,下面是對應表(當然,相關資源的綁定也做好了)         

.properties檔案(key)                Action 欄位(value)
 ---------------                                --------------------------------
 xxx.Action.text                          Action.NAME
 xxx.Action.mnemonic              Action.MNEMONIC_KEY
 xxx.Action.accelerator            Action.ACCELERATOR_KEY
 xxx.Action.icon                        Action.SMALL_ICON,LARGE_ICON_KEY
 xxx.Action.smallIcon               Action.SMALL_ICON
 xxx.Action.largeIcon               Action.LARGE_ICON
 xxx.Action.shortDescription  Action.SHORT_DESCRIPTION
 xxx.Action.longDescription   Action.LONG_DESCRIPTION
 xxx.Action.command               Action.ACTION_COMMAND_KEY
     
 ex. button.Action.text = Hello!  ->  Action.Name = Hello!

2. 將類別中,標記為@Action的方法名稱,傳給ActionMap作為Action的名稱。

預設上,在.properties檔案中的xxx必須和標記為@Action的方法名稱一樣,這樣才可以為某元件取得相對應的Action若要不一樣,則需經由指定的方式。
    
ps. getContext().getActionMap()取得的其實是ApplicationActionMap的實體(繼承ActionMap)

3. 在取得ActionMap實體後,就可以利用get(Object key)來為元件設置Action

button.setAction(map.get(Object key))
      
ActionManager主要負責管理新的Action事件機制 - @Action
      
在取得ActionMap實體後,它會儲存類別中所有被標記成@Action的方法,方便之後的使用。
      
因此,參數中的key值,就必須是它儲存的方法名稱。

下面是個簡單的例子

      
import java.awt.GridLayout;

import javax.swing.ActionMap;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.jdesktop.application.Action;
import org.jdesktop.application.SingleFrameApplication;

public class NewActionDemo1 extends SingleFrameApplication {
    JPanel p;
    @Override
    protected void startup() {        
        ActionMap map = getContext().getActionMap();
        
        p = new JPanel();
        p.setName("pane2");
        p.setLayout(new GridLayout());        
        JButton button1 = new JButton(map.get("drawLine"));        
        JButton button2 = new JButton(map.get("drawCircle"));        
        JButton button3 = new JButton(map.get("drawRectangle"));        
        p.add(button1);
        p.add(button2);
        p.add(button3);            
        show(p);    
    }

    @Action
    public void drawLine() {
        JOptionPane.showMessageDialog(null,"實作畫線的方法");
    }

    @Action
    public void drawCircle() {        
        JOptionPane.showMessageDialog(null,"實作畫圓的方法");
    }    
    
    @Action
    public void drawRectangle() {
        JOptionPane.showMessageDialog(null,"實作畫方的方法");
    }
    
    public static void main(String[] args) {
        launch(NewActionDemo1.class, args);
    }
}

.properties檔案內容如下:    
    
drawLine.Action.text = 畫線
drawLine.Action.shortDescription = 畫線
    
drawCircle.Action.text = 畫圓
drawCircle.Action.shortDescription = 畫圓
    
drawRectangle.Action.text = 畫方
drawRectangle.Action.shortDescription = 畫方拉


若是方法名稱不要和.properties檔案中的key一樣,即需指定key的名稱

    
import javax.swing.ActionMap;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.jdesktop.application.Action;
import org.jdesktop.application.SingleFrameApplication;

public class SpecifyActionDemo extends SingleFrameApplication {

    @Override
    protected void startup() {
        ActionMap map = getContext().getActionMap();

        JPanel p = new JPanel();
        p.setName("panel");
        JButton button1 = new JButton(map.get("Default"));
        JButton button2 = new JButton(map.get("SpecifyName"));
        p.add(button1);
        p.add(button2);
        show(p);
    }

    @Action //等同於@Action(name="")
    public void Default() {
        JOptionPane.showMessageDialog(null, "預設執行的@Action方法,等同
        於\n+"<html><font color=#FF0000>@Action(name=\"  \")</html>");
    }

    @Action(name = "SpecifyName")
    public void It_Is_Specify() { //方法名稱不需要和key一樣
        JOptionPane.showMessageDialog(null,"當方法名稱不要和.properties
        檔案中的key一樣時,可以另外指定@Action要找的key\n+<html>
        <font color=#FF0000>@Action(name=\"xxx\")<br>xxx:指定的名稱
        </html>");
    }

    public static void main(String[] args) {
        launch(SpecifyActionDemo.class, args);
    }
}

.properties檔案內容如下:

Application.title = 指定@Action的方式

Default.Action.text = 預設@Action
Default.Action.shortDescription = 按下去會執行:  預設的@Action方法

SpecifyName.Action.text = 指定@Action
SpecifyName.shortDescription = 按下去會執行:  指定的@Action方法【@Action(name="xxx")】


標記為@Action的方法可以帶有參數, 預設有:
    

ActionEvent               actionEvent
javax.swing.Action    this ApplicationAction object
ActionMap                 the ActionMap that contains this Action
ResourceMap             the ResourceMap of the the ActionMap that contains this Action
ApplicationContext   the value of ApplicationContext.getInstance()

    
下面是帶有參數的例子

    
import java.awt.GridLayout;
import java.awt.event.ActionEvent;

import javax.swing.ActionMap;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;

public class NewActionWithPDemo1 extends SingleFrameApplication {
    JPanel p;
    @Override
    protected void startup() {        
        ActionMap map = getContext().getActionMap();
        
        p = new JPanel();
        p.setName("pane2");
        p.setLayout(new GridLayout());        
        JButton button1 = new JButton(map.get("drawLine"));        
        JButton button2 = new JButton(map.get("drawCircle"));        
        JButton button3 = new JButton(map.get("drawRectangle"));        
        p.add(button1);
        p.add(button2);
        p.add(button3);            
        show(p);    
    }

    @Action
    public void drawLine(ActionEvent e) {
        JOptionPane.showMessageDialog(null,((JButton)e.getSource()).getText());
    }

    @Action
    public void drawCircle(ActionMap action) {        
        JOptionPane.showMessageDialog(null,action.allKeys());
    }    
    
    @Action
    public void drawRectangle(ResourceMap resourceMap) {
        //因為資源管理(綁定)的工作已經做好,所以可以直接使用        JOptionPane.showMessageDialog(null,resourceMap.getString("swing"));
    }
    
    public static void main(String[] args) {
        launch(NewActionWithPDemo1.class, args);
    }
}

.properties檔案內容如下:    

pane2.size = 200,200
  
drawLine.Action.text = 畫線
drawLine.Action.shortDescription = 畫線
    
drawCircle.Action.text = 畫圓
drawCircle.Action.shortDescription = 畫圓
    
drawRectangle.Action.text = 畫方
drawRectangle.Action.shortDescription = 畫方
    
swing = 畫方! 不是畫圓! 你到底知不知!
    

當然,中文也行...Orz

import javax.swing.ActionMap;
import javax.swing.JButton;
import javax.swing.JPanel;

import org.jdesktop.application.Action;
import org.jdesktop.application.SingleFrameApplication;


public class NewActionDemo2 extends SingleFrameApplication{

    @Override
    protected void startup() {
        ActionMap map = getContext().getActionMap();
        
        JPanel p = new JPanel();
        p.setName("panel");        
        JButton button1 = new JButton(map.get("開始"));
        JButton button2 = new JButton(map.get("結束"));        
        p.add(button1);
        p.add(button2);        
        show(p);
    }

    @Action
    public void 開始(){
        System.out.println("中文方法測試:開始");
    }
    @Action
    public void 結束(){
        System.out.println("中文方法測試:結束");
    }
    public static void main(String[] args) {
        launch(NewActionDemo2.class, args);
    }
}

.properties檔案內容如下:    
    
Application.title =中文方法也行 Orz

開始.Action.text = 開始
開始.Action.shortDescription = Start!

結束.Action.text = 結束
結束.Action.shortDescription = End

    
此外,當@Action方法被觸發時,可以利用@Action參數,決定元件是否Enabled、Selected。

【Enabled】     
 @Action(enabledProperty  = "Enabled")    
    
【Selected】     
 @Action(selectedProperty  = "Selected")    

    
@Action加上(enabledProperty  = "Enabled") 或(enabledProperty  = "Selected")時。它會尋找類別中isEnabled()或isSelected()方法,並且利用方法的回傳值(true or false),來決定觸發此方法的元件是否Enabled或Selected。


    public boolean isEnabled() {
        return false; //or true
    }
     
    public boolean isSelected() {
        return false; //or true
   }

ps.存取權限一定要用public,且不可以帶有參數

迴響:

發表迴響:
迴響功能已被關閉