0 Android提供了一个供开发者学习使用的示例程序。其界面如下。图中可以看到,应用列表应为ListView,看其源码发现,并非为简单的ListView,而是采用动态加载的方式。
1 主界面代码如下:
ApiDemos
1 /*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.example.android.apis;
18
19 import android.app.ListActivity;
20 import android.content.Intent;
21 import android.content.pm.PackageManager;
22 import android.content.pm.ResolveInfo;
23 import android.os.Bundle;
24 import android.view.View;
25 import android.widget.ListView;
26 import android.widget.SimpleAdapter;
27
28 import java.text.Collator;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35
36 public class ApiDemos extends ListActivity {
37
38 @Override
39 public void onCreate(Bundle savedInstanceState) {
40 super.onCreate(savedInstanceState);
41
42 Intent intent = getIntent();
43 String path = intent.getStringExtra("com.example.android.apis.Path");
44
45 if (path == null) {
46 path = "";
47 }
48
49 setListAdapter(new SimpleAdapter(this, getData(path),
50 android.R.layout.simple_list_item_1, new String[] { "title" },
51 new int[] { android.R.id.text1 }));
52 getListView().setTextFilterEnabled(true);
53 }
54
55 protected List getData(String prefix) {
56 List<Map> myData = new ArrayList<Map>();
57
58 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
59 mainIntent.addCategory(Intent.CATEGORY_SAMPLE_CODE);
60
61 PackageManager pm = getPackageManager();
62 List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);
63
64 if (null == list)
65 return myData;
66
67 String[] prefixPath;
68
69 if (prefix.equals("")) {
70 prefixPath = null;
71 } else {
72 prefixPath = prefix.split("/");
73 }
74
75 int len = list.size();
76
77 Map<String, Boolean> entries = new HashMap<String, Boolean>();
78
79 for (int i = 0; i < len; i++) {
80 ResolveInfo info = list.get(i);
81 CharSequence labelSeq = info.loadLabel(pm);
82 String label = labelSeq != null
83 ? labelSeq.toString()
84 : info.activityInfo.name;
85
86 if (prefix.length() == 0 || label.startsWith(prefix)) {
87
88 String[] labelPath = label.split("/");
89
90 String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length];
91
92 if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) {
93 addItem(myData, nextLabel, activityIntent(
94 info.activityInfo.applicationInfo.packageName,
95 info.activityInfo.name));
96 } else {
97 if (entries.get(nextLabel) == null) {
98 addItem(myData, nextLabel, browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel));
99 entries.put(nextLabel, true);
100 }
101 }
102 }
103 }
104
105 Collections.sort(myData, sDisplayNameComparator);
106
107 return myData;
108 }
109
110 private final static Comparator<Map> sDisplayNameComparator = new Comparator<Map>() {
111 private final Collator collator = Collator.getInstance();
112
113 public int compare(Map map1, Map map2) {
114 return collator.compare(map1.get("title"), map2.get("title"));
115 }
116 };
117
118 protected Intent activityIntent(String pkg, String componentName) {
119 Intent result = new Intent();
120 result.setClassName(pkg, componentName);
121 return result;
122 }
123
124 protected Intent browseIntent(String path) {
125 Intent result = new Intent();
126 result.setClass(this, ApiDemos.class);
127 result.putExtra("com.example.android.apis.Path", path);
128 return result;
129 }
130
131 protected void addItem(List<Map> data, String name, Intent intent) {
132 Map<String, Object> temp = new HashMap<String, Object>();
133 temp.put("title", name);
134 temp.put("intent", intent);
135 data.add(temp);
136 }
137
138 @Override
139 protected void onListItemClick(ListView l, View v, int position, long id) {
140 Map map = (Map) l.getItemAtPosition(position);
141
142 Intent intent = (Intent) map.get("intent");
143 startActivity(intent);
144 }
145
146 }
下面为部分配置文件代码:
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Declare the contents of this Android application. The namespace
attribute brings in the Android platform namespace, and the package
supplies a unique name for the application. When writing your
own application, the package name must be changed from "com.example.*"
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.apis">
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.NFC" />
<!-- For android.media.audiofx.Visualizer -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- We will request access to the camera, saying we require a camera
of some sort but not one with autofocus capability. -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<application android:name="ApiDemosApplication"
android:label="@string/activity_sample_code"
android:icon="@drawable/app_sample_code" >
<!-- This is how we can request a library but still allow the app
to be installed if it doesn't exist. -->
<uses-library android:name="com.example.will.never.exist" android:required="false" />
<activity android:name="ApiDemos">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- ************************************* -->
<!-- APPLICATION PACKAGE SAMPLES -->
<!-- ************************************* -->
<!-- Activity Samples -->
<activity android:name=".app.HelloWorld" android:label="@string/activity_hello_world">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.DialogActivity"
android:label="@string/activity_dialog"
android:theme="@android:style/Theme.Dialog">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.CustomDialogActivity"
android:label="@string/activity_custom_dialog"
android:theme="@style/Theme.CustomDialog">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.QuickContactsDemo"
android:label="@string/quick_contacts_demo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
</application>
<instrumentation android:name=".app.LocalSampleInstrumentation"
android:targetPackage="com.example.android.apis"
android:label="Local Sample" />
</manifest>
2 分析
其想法主要分为以下几步:
1) 给想要显示在列表中的Activity分类
2) 然后从xml中根据分类标签搜索出来
3) 将结果加载到List<Map>中
4) 据此list生成ListView并显示
1) 想达到分类的效果,我们可以在Manifest.xml中对Activity做如下配置,稍后可根据category值进行过滤搜索。在自己的应用中,我们可以自定义category值。
<activity android:name=".app.HelloWorld" android:label="@string/activity_hello_world">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.SAMPLE_CODE"/>
</intent-filter>
</activity>
2) 根据category值查询出想显示的activity对应的ResolveInfo值列表。ResolveInfo对象包含Manifest.xml中对应Activity节点的所有信息,稍后可以根据这些信息对其进行加载。
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_SAMPLE_CODE);
PackageManager pm = getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);
3) 根据查询出的activity的ResolveInfo list,生成List<Map>,其中Map包含title和intent
//10 得到父节点级别数组
String[] prefixPath;
if (prefix.equals("")) {
prefixPath = null;
} else {
prefixPath = prefix.split("/");
}
int len = list.size();
Map<String, Boolean> entries = new HashMap<String, Boolean>();
//11 对过滤得到的ResolveInfo列表进行遍历
for (int i = 0; i < len; i++) {
//111 得到ResolveInfo对象
ResolveInfo info = list.get(i);
CharSequence labelSeq = info.loadLabel(pm);
//112 根据ResolveInfo对象得到Activity对应的label信息,如App/Activity/Hello World
String label = labelSeq != null
? labelSeq.toString()
: info.activityInfo.name;
//113 判断是否有子节点存在
if (prefix.length() == 0 || label.startsWith(prefix)) {
//114 得到字节点的层级数组,如{"App","Activity","Hello World"}
String[] labelPath = label.split("/");
//115 得到应显示层级的标签,如 App
String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length];
//116 当前节点无子节点,且不为第一个级别,将对应Activity信息和标签信息组装成intent,添加至List<Map>
if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) {
addItem(myData, nextLabel, activityIntent(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name));
} else {//116 当前节点有子节点或为第一级节点
//117 判断是否在父节点数组中
if (entries.get(nextLabel) == null) {
//118 将当前activity和节点路径信息一起组装成intent,添加至List<Map>
addItem(myData, nextLabel, browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel));
//119 将此节点路径加到父节点数组中
entries.put(nextLabel, true);
}
}
}
}
4) 根据List<Map>生成ListView
setListAdapter(new SimpleAdapter(this, getData(path),
android.R.layout.simple_list_item_1, new String[] { "title" },
new int[] { android.R.id.text1 }));
getListView().setTextFilterEnabled(true);