標籤:
什麼是Alternative menu(替代菜單)
舉個例子,Activity顯示一個文字檔。如果使用者想對文字檔進行編輯,Activity不提供編輯能力,但可由其他activity或者其他應用提供。我們將相關資訊儲存在一個intent中,例如該文本的Uri。這個intent可以匹配系統的多個應用,替代菜單將這些應用一一列出,功能表項目的title就是該可被調用的activity的名字,表徵圖也為該可被調用的activity的圖表。
小例子說明
我們通過一個小例子進行學習,簡單地開啟一個URL:wei://flowingflying/helloworld。在之前Intent的學習中,我們通過schema的配置,匹配該URL,也就是我們已經有其他應用的Activity(Intent Basic Test)可以開啟該URL。我們同時在App中新增一個activity也能開啟該URL。這樣,將在alternative菜單中加入兩個功能表項目,點擊它們,將開啟相應的activity,並通過intent傳遞相關的資料資訊。
新增的acitivity名字為Invoke Action(好像應該是invoked才對,不好意思)。在AndroidManifest.xml中加入intent-fliter的描述即可,具體見:Pro Android學習筆記(十一):瞭解Intent(中) 。
<activity android:name=".InvokeAction" android:label="@string/invokeAction" android:icon="@drawable/leaf" >
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<data android:scheme="wei" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.ALTERNATIVE" /><!-- 將在最後討論 -->
</intent-filter>
</activity>
Alternative menu代碼
我們看看如何將替代菜單加入到OptionMenu中。Alternative menu還可以載入subMenu,Context Menu中。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// 對比:加入一個普通功能表項目
menu.add("普通功能表項目");
//【步驟1】設定intent,本例簡單實用一個已知的Uri。
Intent menuIntent = new Intent(null,Uri.parse("wei://flowingflying/helloWorld"));
//【步驟2】加入Alternative菜單。在之前的Item ID類別中已經講過,Android對ID進行了劃分,有alternative的ID範圍。
int menuGroup = Menu.CATEGORY_ALTERNATIVE;
int startingItemId = Menu.CATEGORY_ALTERNATIVE;
int orderId = Menu.CATEGORY_ALTERNATIVE;
menu.addIntentOptions( //返回增加的功能表項目數目,本例為2
menuGroup, /* int groupId */
startingItemId, /* int itemId:由於自動跳轉到,此參數可以設定為Menu.NONE。 */
orderId, /*int order*/
this.getComponentName(), /* ComponentName caller:當前的activity名字,這是android系統處理alternatice menu是調用的queryIntentActivityOptions()函數所需要的參數。getComponentName()返回package名字和class名字,系統以此獲知源activity是誰。 */
null, /* Intent[] specifics:匹配可能有多個intent,此用於過濾,但具體用途不詳 */
menuIntent, /* Intent intent:關鍵的intent */
0, /* flages:關於items如何加入。0表示 no flag*/
null); /* MenuItem[] outSpecificItems ,與specifice相關*/
return super.onCreateOptionsMenu(menu);
}
關於Category和規範代碼寫法
我們注意到,在被喚起的actvity中有下面的描述:
[html] view plaincopy
- <category android:name="android.intent.category.ALTERNATIVE" />
在實驗中,發現此項可有可無,並不真正影響結果。而在reference中卻明確表示要為CATEGORY_ALTERNATIVE或者CATEGORY_SELECTED_ALTERNATIVE。為何?
我們以Alternative menu的方式調用其他activity,正規的做法是,被喚起的activity應允許被alternative菜單喚起。因此被喚起的activity在intent-fliter中需給出類別。同時alternative菜單的intent也應當標明自己類型。因此規範的代碼是:
Intent menuIntent = new Intent(null,this.getIntent().getData());
menuIntent.addCategory(Intent.CATEGORY_ALTERNATIVE);
在小例子中,由於其他應用的Activity(Intent Basic Test)在Manifest XML中並沒有給出相應的類別,不被匹配。運行結果
關於flags
menu.addIntentOptions()的倒數第二個參數是flags,表示功能表項目添加的方式。0,即預設,表示如果groupId相同,則替代菜單將取代原有的功能表項目設定。如果我們想保留原有的同一Group的功能表項目,可以將flags設定為Menu.FLAG_APPEND_TO_GROUP。注意,如果groupId為Menu.NONE是不進行替換的,這個表示不設定GroupId,並非GroupId為0。
多個匹配的itemId等參數
讓我們看看系統是如何?Alternative菜單的。從reference中看到,Menu是一個interface,具體是通過MenuBuilder實現(原始碼見android-17(version)/com/android/internal/view/menu/MenuBuilder.java。相關代碼如下:
public int addIntentOptions(int group, int id, int categoryOrder, ComponentName caller,
Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
PackageManager pm = mContext.getPackageManager();
final List<ResolveInfo> lri = //查詢匹配的Activity資訊
pm.queryIntentActivityOptions(caller, specifics, intent, 0);
final int N = lri != null ? lri.size() : 0;
//下面說明如果flag表示FLAG_APPEND_TO_GROUP,會刪除整個group,取而代之
if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
removeGroup(group);
}
for (int i=0; i<N; i++) {
final ResolveInfo ri = lri.get(i);
Intent rintent = new Intent(
ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]);
rintent.setComponent(new ComponentName(
ri.activityInfo.applicationInfo.packageName,
ri.activityInfo.name));
final MenuItem item = add(group, id, categoryOrder, ri.loadLabel(pm))
.setIcon(ri.loadIcon(pm))
.setIntent(rintent);
if (outSpecificItems != null && ri.specificIndex >= 0) {
outSpecificItems[ri.specificIndex] = item;
}
}
return N;
}
從原始碼,可能看出如果有多個匹配,這些功能表項目具有相同的group,相同的id,和相同categoryOrder。雖然我們在小例子中使用了startingItemId,但是實際上itemId是相同的。在小例子中,我們增加了public boolean onOptionsItemSelected(MenuItem item),並在裡面檢查item的參數值,證實確實相同。
這段代碼還說明了功能表項目的名字和圖片為何,以及為何能喚起Activity。採用了setIntent(),是在Pro Android學習筆記(三十):Menu(1):瞭解Menu學習過的的一種觸發機制。
本博文涉及的例子代碼,可以在Pro Android學習:Menu中下載。
Pro Android學習筆記(三三):Menu(4):Alternative菜單