地址:http://www.cnblogs.com/Steve-Kale/p/3775406.html
今天,我们来了解一下把匿名动作作为菜单项集成。首先,我们需要了解Intent。Intent作为应用程序组件之间消息的传递方法。分为显式Intent和隐式Intent。
显式Intent是直接将应用程序组件引用作为参数传递给Intent,从而调用该应用组件。隐式Intent则是通过Intent Filter来选择对应的应用组件。在不知道应用组件名,或是在设备范围内广播信息,应该使用隐式Intent。例如,你在自己编写的程序中需要使用系统的应用组件或者来自第三方程序的组件,你可以通过sendBroadcast(intent)来发送消息(请求)。Android会通过对比发送的intent与Intent Filter,根据action,category,data过滤掉不符合要求的应用程序。
隐式Intent有两种使用方法,一种是给定action,与Intent Filter中的action比对(若Intent Filter没有任何动作或不包含该动作,则视为不匹配)。从而调用相应的应用组件。
另一种就是我们今天要说的,Intent不设定action,只给定特定的data类型,通过Intent Filter来选择可以处理该类型数据的应用组件。这种机制使现有应用程序可以匿名使用Activity的动作。
举个例子,如果你N年前编写了一个程序,现在需要对某种类型的数据处理增加新的功能,那么你现在只需要创建那个功能组件就好了。是不是很强大?好了,说了这么多,该切入正题了。
首先,你完成了一个功能组件的编写,在manifest里进行声明。(例如用于控制从轨道上对月球进行核攻击的Activity)
然后,在需要调用它的程序中设置intent,指定待处理的数据uri,类型。
最后在onCreateOptionsMenu或者onPrepareOptionsMenu中调用menu.addIntentOptions把设置好的intent作为参数传递给menu就好了。
下贴有示例代码。在Main activity中显示捕获的intent filter的名称。即为<manifest>-><activity>-><intent-filter>->label.
Main activity
1 package com.example.IntentFilter; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.app.Activity; 7 import android.content.ComponentName; 8 import android.content.Intent; 9 import android.content.pm.PackageManager; 10 import android.content.pm.ResolveInfo; 11 import android.content.res.Resources; 12 import android.os.Bundle; 13 import android.provider.Contacts; 14 import android.view.Menu; 15 import android.view.MenuItem; 16 import android.widget.ArrayAdapter; 17 import android.widget.ListView; 18 import android.widget.Toast; 19 20 public class Main extends Activity { 21 private Intent intent; 22 public void onCreate(Bundle savedInstanceState){ 23 24 super.onCreate(savedInstanceState); 25 PackageManager pm=getPackageManager(); 26 setContentView(R.layout.main); 27 ListView lv=(ListView)findViewById(R.id.list); 28 //设置指定数据类型的intent,为了筛选intent filter。 29 intent=new Intent(); 30 intent.setData(Contacts.CONTENT_URI); 31 intent.addCategory(Intent.CATEGORY_ALTERNATIVE); 32 List<ResolveInfo> actions; 33 34 //获得解析intent,得到的intent filter的信息。 35 actions=pm.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY ); 36 37 ArrayList<String> labels=new ArrayList<String>(); 38 //获取intent filter 的label属性的string值。为 xml资源,可通过resources取出里面的string值。 39 Resources r=getResources(); 40 for(ResolveInfo action: actions){ 41 labels.add(r.getString(action.labelRes)); 42 } 43 44 lv.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, labels)); 45 } 46 //创建动态菜单, 47 public boolean onCreateOptionsMenu(Menu menu) { 48 int menuGroup=0; 49 int menuItemId=0; 50 int menuItemOrder=Menu.NONE; 51 ComponentName caller=getComponentName();//提供调用动作的组件名称。通常为当前activity 52 Intent[] specificIntents=null;//首先定义应该添加的intent 53 int flag=Menu.FLAG_APPEND_TO_GROUP;//设置任意的选项标识 54 MenuItem[] outSpecificItems=null;//通过前面specificIntent和intent创建的菜单项填充此数组 55 menu.addIntentOptions(menuGroup, menuItemId, menuItemOrder, caller, specificIntents, intent, flag, outSpecificItems); 56 return true; 57 } 58 }
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package="com.example.IntentFilter" 3 android:versionCode="1" 4 android:versionName="1.0" > 5 6 <uses-sdk 7 android:minSdkVersion="8" 8 android:targetSdkVersion="19" /> 9 <uses-permission android:name="android.permission.READ_CONTACTS"/> 10 11 <application 12 android:allowBackup="true" 13 android:icon="@drawable/ic_launcher" 14 android:label="@string/app_name" 15 android:theme="@style/AppTheme" > 16 <activity 17 android:name=".one" 18 android:label="one"> 19 20 <intent-filter> 21 <action android:name="android.intent.action.PICK"/> 22 <category android:name="android.intent.category.DEFAULT"/> 23 24 </intent-filter> 25 <!-- 注意每个intent-filter里面的属性是独立的,在主组件设置PackageManager.MATCH_DEFAULT_ONLY, 26 则一定在提供筛选项的intent filter设置default才能保证检测到 --> 27 <intent-filter android:label="@string/filter_label"> 28 29 <action android:name="com.Kale.example.READ_CONTACK"/> 30 <category android:name="android.intent.category.DEFAULT" /> 31 <category android:name="android.intent.category.ALTERNATIVE"/> 32 <data android:path="contacts" android:scheme="content"/> 33 </intent-filter> 34 35 </activity> 36 37 <activity 38 android:name=".Main" 39 android:label="main"> 40 <intent-filter> 41 <action android:name="android.intent.action.MAIN" /> 42 <category android:name="android.intent.category.LAUNCHER" /> 43 </intent-filter> 44 </activity> 45 </application> 46 47 </manifest>
需要注意的是,intent-filter是独立的。如图,功能组件one中有两个intent-filter。其中第二个是提供进行为数据操作的筛选的。我们在Main activity中queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY );设置为,当intent-filter含有DEFAULT时,才能成功筛选出。而在第一个intent-filter中定义的DEFAULT不起作用。我们可以这样考虑,在intent解析过程中,intent没有指定活动action,所以intent-filter含有的action不对过滤造成影响。比对intent-filter,不含有intent指定的类型category,则被淘汰。然后比对data,第一个intent-filter的category和data都不匹配。第二个不含有DEFAULT,所以被淘汰。而在创建动态菜单时,当第二个intent-filter不含有DEFAULT时,为什么还会出现具有one功能的菜单选项呢?
原因很简单,因为我们在构建动态菜单时,并没有指定intent为DEFAULT,所以说,第二个intent-filter与intent匹配,自然会出现对应的菜单选项。