Pro Android学习笔记(三三):Menu(4):Alternative菜单

什么是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

  1. <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中下载。

时间: 2024-10-07 00:13:56

Pro Android学习笔记(三三):Menu(4):Alternative菜单的相关文章

Pro Android学习笔记(二九):用户界面和控制(17):include和merge

xml控件代码重用:include 如果我们定义一个控件,需要在不同的layout中重复使用,或者在同一个layout中重复使用,可以采用include的方式.例如定义my_button.xml如下 <?xml version="1.0" encoding="utf-8"?> <Button xmlns:android="http://schemas.android.com/apk/res/android"     androi

Pro Android学习笔记 ActionBar(1):Home图标区

 Pro Android学习笔记(四八):ActionBar(1):Home图标区 2013年03月10日 ? 综合 ? 共 3256字 ? 字号 小 中 大 ? 评论关闭 ActionBar在Android 3.0 SDK中为平板引入,在4.0中也可以在phone中使用.在title中提供类似tab和菜单的效果,有三种形式:Tabbed action bar,list action bar和standard action bar,我们将在小例子中进行示范. Home Icon 在Actio

【转】 Pro Android学习笔记(五二):ActionBar(5):list模式

可以在action bar中加入spinner的下来菜单,有关spinner,可以参考Pro Android学习笔记(二十):用户界面和控制(8):GridView和Spinner. list的样式和theme有关,如果theme设置不正确,还可能会出现异常. 相关的代码如下: public class ListActionBarDemo extends SearchTestCase3 implements OnNavigationListener{ //List触发的回调函数接口    @Ov

【转】 Pro Android学习笔记(二九):用户界面和控制(17):include和merge

目录(?)[-] xml控件代码重用include xml控件代码重用merge 横屏和竖屏landsacpe portrait xml控件代码重用:include 如果我们定义一个控件,需要在不同的layout中重复使用,或者在同一个layout中重复使用,可以采用include的方式.例如定义my_button.xml如下 <?xml version="1.0" encoding="utf-8"?> <Button xmlns:android=

【转】 Pro Android学习笔记(四二):Fragment(7):切换效果

目录(?)[-] 利用setTransition 利用setCustomAnimations 通过ObjectAnimator自定义动态效果 程序代码的编写 利用fragment transaction进行切换,很方便提供切换的效果. 利用setTransition() 在Pro Android学习笔记(三九):Fragment(4):基础小例子-续的“Step 4:实现showDetail(int index),如何管理fragment”中,介绍了如何在容器FrameLayout中通过frag

【转】Pro Android学习笔记(二五):用户界面和控制(13):LinearLayout和TableLayout

目录(?)[-] 布局Layout 线性布局LinearLayout 表格布局TableLayout 布局Layout Layout是容器,用于对所包含的view进行布局.layout是view的子类,所以可以作为view嵌入到其他的layout中.Android的layout有LinearLayout.TableLayout,RelativeLayout.FrameLayout.GridLayout. 线性布局:LinearLayout 这是最常用的,有anroid:orientation来确

【转】 Pro Android学习笔记(四十):Fragment(5):适应不同屏幕或排版

目录(?)[-] 设置横排和竖排的不同排版风格 改写代码 对于fragment,经常涉及不同屏幕尺寸和不同的排版风格.我们在基础小例子上做一下改动,在横排的时候,仍是现实左右两个fragment,在竖排时,如下图显示: 屏幕上只显示一个fragment,点击列表上的数目,进入到简介的activity.下面介绍实现的方式. 设置横排和竖排的不同排版风格 在 Pro Android学习笔记(四):了解Android资源(下)的“资源和配置的变更”中,我们介绍了如何同资源文件夹名设置不同资源.缺省的l

【转】 Pro Android学习笔记(十九):用户界面和控制(7):ListView

目录(?)[-] 点击List的item触发 添加其他控件以及获取item数据 ListView控件以垂直布局方式显示子view.系统的android.app.ListActivity已经实现了一个只含有一个ListView的Activity,并通过setListAdapter()方法来管理adapter.我们可以通过扩展ListActivity来实现. 我们要在整个屏幕上显示ListView,我们直接继承使用ListActivity,不需要在定义自己的layout XML文件,我们在上一学习中

【转】 Pro Android学习笔记(八二):了解Package(1):包和进程

文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在之前,我们已经学习了如何签发apk,见Pro Android学习笔记(六四):安全和权限(1):签发apk,我们将对package做进一步了解. 每个apk都有一个唯一的根包名,在AndroidManifest.xml中定义,如下.开发者为包进行签发,前面和包名绑定,其他开发者不能对这个包进行更新. <?xml version="1