Swing Application Framework 簡單介紹(四)
Actions
Swing Application Framework 打算利用Anntatin - @Action來取代ActionListener中的actionPerformed(ActionEvent e)方法。
在這之前,如果我們要為元件增加ActionListener,不是實作ActionListener()中的actionPerformed(ActionEvent e)方法,就是利用Action/ActionMap。
但是不論那一種,使用上還是不太方便。
一、方法名稱的問題:
必須再到實作事件的地方,才能清楚某元件(準備)要做什麼事。
button.addActionListener() // <- 不能夠直接反應(說明)button要做的事
二、EDT的問題:
在處理事件的過程式,還必須小心是否違反EDT的原則,或者在程式碼內穿插一堆檢查的機制。
//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實體來利得相對應的ActionMap實體
當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);
}
}
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);
}
}
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.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,且不可以帶有參數