Android中的常量 DEFAULT_KEYS_SHORTCUT

1.  關於 DEFAULT_KEYS_SHORTCUT  的 API文檔介紹
Use with setDefaultKeyMode(int) to execute a menu shortcut in default key handling.

That is, the user does not need to hold down the menu key to execute menu shortcuts.

 

從字面上看,其含義是指,將默認的按鍵輸入作為菜單快捷鍵進行處理。

也就是說,用戶不需要按下menu按鍵,就可以處理菜單快捷鍵,聽起來非常神奇,究竟是不是這樣呢?

 

 

——————————————————————————–

2.編寫示例程序
我們編寫一個程序驗證一下其功能,首先新建一個工程,並設置默認按鍵模式為 DEFAULT_KEYS_SHORTCUT

 

[java]
package com.silenceburn; 
 
import android.app.Activity; 
import android.os.Bundle; 
 
public class MenuShortCutTester extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
         
        setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT); 
    } 

 

 

 

為默認的main.xml中的TextView增加一個id屬性,之後我們會用菜單選項控制這行字的顏色

 

[xhtml] 
 
http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    > 
<textview  
    android:id="@+id/myText"  
    android:layout_width="fill_parent"  
    android:layout_height="wrap_content"  
    android:text="@string/hello" 
    /> </textview  

 

 

 

 

使用findViewById獲取上一步中定義瞭id的文本對象,將其引用保存在成員變量b中。

重寫onPrepareOptionsMenu方法,增加我們自己的菜單項,並註冊快捷鍵,同時增加菜單點擊的響應事件。

 

[java] 
package com.silenceburn; 
 
import android.app.Activity; 
import android.os.Bundle; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.view.MenuItem.OnMenuItemClickListener; 
import android.widget.TextView; 
 
public class MenuShortCutTester extends Activity { 
    /** Called when the activity is first created. */ 
    TextView b; 
     
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
         
        b = (TextView) this.findViewById(R.id.myText); 
         
        setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT); 
    } 
     
    @Override 
    public boolean onPrepareOptionsMenu(Menu menu) { 
        // TODO Auto-generated method stub 
        super.onPrepareOptionsMenu(menu); 
         
        menu.removeItem(0); 
        menu.removeItem(1); 
        menu.add( 0, 0, 0, "One").setShortcut('0', '0').setOnMenuItemClickListener(new OnMenuItemClickListener(){ 
 
            @Override 
            public boolean onMenuItemClick(MenuItem item) { 
                // TODO Auto-generated method stub 
                b.setBackgroundColor(android.graphics.Color.RED); 
                return true; 
            }}); 
        menu.add( 0, 1, 0, "Two").setShortcut('1', '1').setOnMenuItemClickListener(new OnMenuItemClickListener(){ 
 
            @Override 
            public boolean onMenuItemClick(MenuItem item) { 
                // TODO Auto-generated method stub 
                b.setBackgroundColor(android.graphics.Color.GREEN); 
                return true; 
            }});  
         
        return true; 
    } 

 

 

 

註意我們一共註冊瞭兩個菜單項,

一個叫“One”,點擊時將文本對象 b 的背景顏色改為紅色,同時定義其快捷鍵為0

一個叫“Two”,點擊時將文本對象 b 的背景顏色改為綠色,同時定義其快捷鍵為1

 

至此示例程序完成。

 

——————————————————————————–

3.驗證使用示例程序
啟動AVD,運行上述程序,程序啟動後,我們應當看到是黑底灰字,點擊menu按鈕,可以看到One和Two兩個菜單選項。

如下圖所示:

 

 

 

目前Menu是打開狀態,

點擊One ,將把“helloworld…”字樣的背景色變為紅色,

點擊Two ,將把“helloworld…”字樣的背景色變為紅綠色。

或者我們點設置好的快捷鍵 0 和 1,發現可以直接調用菜單選項控制顏色變化。

 

到目前為止一切都很正常,不過,神奇的現在來瞭!

 

我們首先關閉菜單,

然後直接點鍵盤鍵"0“,看看會發生什麼。再直接點鍵盤鍵"1" ,看看會發生什麼。

哈哈,在沒有激活菜單的情況下,菜單項快捷鍵被直接調用瞭!根本不需要打開菜單,就可以用激活菜單快捷鍵!

 

什麼?有位同學說快捷鍵就應該是這樣子把,那好,請你把 onCreate 裡面的

setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT); 改為 setDefaultKeyMode(DEFAULT_KEYS_DISABLE);

然後再運行試試,在不打開菜單的情況下,你就是把 0 和 1 按壞,系統也不會理你的呵呵

 

——————————————————————————–

4.淺析實現原理
那麼這神奇的功能是如何實現的呢?我們試著通過分析android源碼找到答案。

 

首先順藤摸瓜,我們找一找系統是如何處理 DEFAULT_KEYS_SHORTCUT 關鍵字的,

在Activity.java中可以找到如下代碼片段:

 

 

[java] 
if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) { 
    return false; 
} else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) { 
    if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,  
            keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) { 
        return true; 
    } 
    return false; 

 

 

由此可知,當系統檢測到 DEFAULT_KEYS_SHORTCUT 關鍵字時,實際調用瞭

getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,

keyCode, event,Menu.FLAG_ALWAYS_PERFORM_CLOSE)

 

我們繼續追尋,但是這裡會遇到一個困難,就是查閱API文檔你會發現,performPanelShortcut函數是個純虛函數!

接下來該怎麼辦呢?既然功能順利執行瞭,那麼這個純虛函數一定會有一個實現的。這個實現類必然是window類的子類。

所以我們在OnCreate裡面加上一行代碼 Window w =  this.getWindow();

然後通過Eclipse的調試器,利用RTTI查看其實現類,結果如下圖:

可以看的很清楚,實現類是 PhoneWindow ,

這樣我們就可以到 PhoneWindow 的源碼中去查找performPanelShortcut的實現瞭。

 

在PhoneWindow.java中我們可以看到如下代碼片段:

 

[java]
// Only try to perform menu shortcuts if preparePanel returned true (possible false 
// return value from application not wanting to show the menu). 
if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) { 
    // The menu is prepared now, perform the shortcut on it 
    handled = st.menu.performShortcut(keyCode, event, flags); 

 

 

終於看到menu字樣瞭,這裡我們可以看到 if 裡面描述的調用條件,

首先當前panel必須已經準備好瞭(你可以用 onPreparePanel 截獲到準備請求),

其次,當前panel必須是有Menu的!(st.menu != null),

從這裡我們可以明白DEFAULT_KEYS_SHORTCUT對於沒有menu的應用是沒有任何效果的。

而且在另一處代碼我們會看到還要進行 isShortCut 的判斷,所以對於沒有快捷鍵的菜單也是沒有任何效果的。

 

那麼我們再看看 preparePanel 裡面是如何實現的,在其實現中可以找到如下代碼片段:

 

[java]
// Callback and return if the callback does not want to show the menu 
if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) { 
    return false; 

 

 

至此,就完全明白瞭!代碼在這裡回調瞭 onPreparePanel ,而 onPreparePanel  中會回調  onPrepareOptionsMenu ,

而onPrepareOptionsMenu ,就是我們自己寫實現自定義菜單的地方瞭。

 

為瞭驗證上述推導,我們在onPrepareOptionsMenu 中放入斷點,然後在菜單關閉的情況下,輸入快捷鍵,

運行到斷點後查看調用堆棧,入下圖所示:

 

 

堆棧調用順序可以很清楚的看出我們的推導過程是正確的。至此 DEFAULT_KEYS_SHORTCUT 的實現分析完畢。

You May Also Like