创建好ApiDemos项目以后,首先在模拟器上运行该程序,可以看到主界面是一个列表。单击列表中一个栏目后还有若干级列表,最终是一个Activity,展示了其API的一个特性。API Demos全面展示了系统的功能,包括界面、控件、图像处理和媒体处理等。
2 < intent-filter >
3 < action android:name ="android.intent.action.MAIN" />
4 < category android:name ="android.intent.category.SAMPLE_CODE" />
5 </ intent-filter >
6 </ activity >
2
3 </ manifest >
可以知道该类在com.example.android.apis下,同时我们可以看到很多Activity都带有
2 < action android:name ="android.intent.action.MAIN" />
3 < category android:name ="android.intent.category.SAMPLE_CODE" />
4 </ intent-filter >
然后我们可以找到com.example.android.apis包下的ApiDemos类,
该类继承了ListActivity,主要用来列出ApiDemos中的200多个实例,实例采取分类层次显示。
ListActivity是一个把列表和列表内容绑定的Activity,在用户单击这些列表时,响应用户操作。这就是说,列表只是一个框架,列表内容是开发者指定的,列表可以和不同的数据源进行绑定。就像这个程序的各级菜单,列表形式都一样,只是每一级菜单的数据源不同。现在我们可以理解APIDemos的主要工作了:在onCreate中获取每一级菜单的列表内容,并和列表绑定;在onListItemClick中实现单击该项目的响应,响应是以Intent方法实现的,通过Intent来启动另外的Activity。
在ApiDemos 的 onCreate()方法:
2 public void onCreate(Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4
5 Intent intent = getIntent();
6 String path = intent.getStringExtra("com.example.android.apis.Path");
7
8 if (path == null) {
9 path = "";
10 }
11
12 setListAdapter(new SimpleAdapter(this, getData(path),
13 android.R.layout.simple_list_item_1, new String[] { "title" },
14 new int[] { android.R.id.text1 }));
15 getListView().setTextFilterEnabled(true);
16 }
2 <!-- Copyright (C) 2006 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 < TextView xmlns:android ="http://schemas.android.com/apk/res/android"
18 android:id ="@android:id/text1"
19 android:layout_width ="match_parent"
20 android:layout_height ="wrap_content"
21 android:textAppearance ="?android:attr/textAppearanceListItemSmall"
22 android:gravity ="center_vertical"
23 android:paddingLeft ="?android:attr/listPreferredItemPaddingLeft"
24 android:paddingRight ="?android:attr/listPreferredItemPaddingRight"
25 android:minHeight ="?android:attr/listPreferredItemHeightSmall"
26 />
可以看到该布局文件仅仅显示一个TextView,它的id为android.R.id.text1
1 AbsListView.setTextFilterEnabled( true );
这个方法的作用是用来过滤选项的。例如在软键盘上打出一个a,则会过滤掉除了a开头的所有选项(在模拟器上可以看到效果,在真机上弹不出软键盘)。
2 List<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();
3
4 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
5 mainIntent.addCategory(Intent.CATEGORY_SAMPLE_CODE);
6
7 PackageManager pm = getPackageManager();
8 List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);
9
10 if (null == list)
11 return myData;
12
13 String[] prefixPath;
14 String prefixWithSlash = prefix;
15
16 if (prefix.equals("")) {
17 prefixPath = null;
18 } else {
19 prefixPath = prefix.split("/");
20 prefixWithSlash = prefix + "/";
21 }
22
23 int len = list.size();
24
25 Map<String, Boolean> entries = new HashMap<String, Boolean>();
26
27 for (int i = 0; i < len; i++) {
28 ResolveInfo info = list.get(i);
29 CharSequence labelSeq = info.loadLabel(pm);
30 String label = labelSeq != null
31 ? labelSeq.toString()
32 : info.activityInfo.name;
33
34 if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) {
35
36 String[] labelPath = label.split("/");
37
38 String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length];
39
40 if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) {
41 addItem(myData, nextLabel, activityIntent(
42 info.activityInfo.applicationInfo.packageName,
43 info.activityInfo.name));
44 } else {
45 if (entries.get(nextLabel) == null) {
46 addItem(myData, nextLabel, browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel));
47 entries.put(nextLabel, true);
48 }
49 }
50 }
51 }
52
53 Collections.sort(myData, sDisplayNameComparator);
54
55 return myData;
56 }
知道列表的构成和响应后,我们接下来看看列表是如何被获取的,获取列表是在getData方法中,该方法获取的数据作为SimpleAdapter的参数,最终被ListActivity所使用。getData中有若干个重要的对象,如myData是我们需要获取的菜单列表数据;prefix是当前所处的目录,如果为空则为根目录; prefixPath是当前目录的列表,列表中包含了当前的每一级目录的内容;list是当前可以被执行的所有Activity列表。程序是怎么知道当前有哪些可以执行的Activity的呢?请看下面两行代码:
2 new Comparator < Map < String, Object >> () {
3 private final Collator collator = Collator.getInstance();
4
5 public int compare(Map<String, Object> map1, Map<String, Object> map2) {
6 return collator.compare(map1.get("title"), map2.get("title"));
7 }
8 } ;
loadLabel() 查找顺序
1、先找activity的Lable, 如果没有找到则下一步
2、找application的Lable, 如果没有找到则下一步
3、找activity的name 名字一定会有的
它通过PackageManager 从 AndroidManifest.xml中读取所以Intent-Filter含有:Intent.ACTION_MAIN和Intent.CATEGORY_SAMPLE_CODE所有Activity信息。前面说过200多个示例根据其功能分类,比如 Hello World示例它的Label为
App/Activity/Hello World,
表示它的分类为分类App下Activity子类。
getData(String prefix)根据每个Activity的Label属性和当前层次(prefix)来决定当前列表中某项为叶子列表项,还是分类列表项,如果是叶子列表项,则添加为activityIntent,当用户点击改列表项时则会触发该示例。若是分类列表项,则添加为browseIntent,browseIntent还是触发ApiDemos Activity,但Intent带有Extra信息,表示需要显示改分类下的子类:
2
3 Intent result = new Intent();
4
5 result.setClassName(pkg, componentName);
6
7 return result;
8
9 }
10
11
此时如果用户点击该节点列表项,则会进入该分类的下级目录。
2 @SuppressWarnings( " unchecked " )
3 protected void onListItemClick(ListView l, View v, int position, long id) {
4 Map<String, Object> map = (Map<String, Object>)l.getItemAtPosition(position);
5
6 Intent intent = (Intent) map.get("intent");
7 startActivity(intent);
8 }
此外,ApiDemos还定义了ApiDemosApplication,该类继承了Application,如果需要在多个Activity共享一些数据,可以定义在Application中。
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.Application;
20
21 /** */ /**
22 * This is an example of a {@link android.app.Application} class. This can
23 * be used as a central repository for per-process information about your app;
24 * however it is recommended to use singletons for that instead rather than merge
25 * all of these globals from across your application into one place here.
26 *
27 * In this case, we have not defined any specific work for this Application.
28 *
29 * See samples/ApiDemos/tests/src/com.example.android.apis/ApiDemosApplicationTests for an example
30 * of how to perform unit tests on an Application object.
31 */
32 public class ApiDemosApplication extends Application {
33 @Override
34 public void onCreate() {
35 }
36}
37
可以看到该类什么都干。
如果使用了自定义的Application,别忘了修改AndroidManifest.xml ,如下:
2 android:label ="@string/activity_sample_code"
3 android:icon ="@drawable/app_sample_code"
4 android:hardwareAccelerated ="true" >
5
6 </ application >
最后我们看下Shakespeare这个常量类,