ApiDemos示例学习(1)——ApiDemos示例的导入
---恢复内容开始---
今天准备开始写这个ApiDemos示例的学习日记了,放在网上以监督自己!
首先是导入该示例。如果我们在配置Android开发环境是,利用Android SDK 安装包中的SDK Manager.exe文件下载完整的包,里面包含各种API版本的Samples,这里使用的是API 16版本中的ApiDemos。
1、导入ApiDemos示例
在eclipse中file->new->android sample project->next,然后在列出的不同版本示例中选择对应版本的ApiDemos示例即可导入示例。成功导入后,可以先看看该示例的结构,在运行该示例。示例结构如下:
可以清楚的看到该工程的目录结构,程序的入口是com.example.android.apis包(这个可以在AndroidManifest.xml文件中查看)的ApiDemos.java文件中。该文件的功能是完成整个示例(包括示例下的所用小示例)的结构设计,十分巧妙,在Mono For Android 的示例包android-app-master包中同样采用相似的结构实现了示例导航的功能。
现在具体分析一下该文件的功能:
1 package com.example.android.apis; 2 3 import android.app.ListActivity; 4 import android.content.Intent; 5 import android.content.pm.PackageManager; 6 import android.content.pm.ResolveInfo; 7 import android.os.Bundle; 8 import android.view.View; 9 import android.widget.ListView; 10 import android.widget.SimpleAdapter; 11 12 import java.text.Collator; 13 import java.util.ArrayList; 14 import java.util.Collections; 15 import java.util.Comparator; 16 import java.util.HashMap; 17 import java.util.List; 18 import java.util.Map; 19 20 public class ApiDemos extends ListActivity { 21 22 @Override 23 public void onCreate(Bundle savedInstanceState) { 24 super.onCreate(savedInstanceState); 25 26 Intent intent = getIntent(); 27 String path = intent.getStringExtra("com.example.android.apis.Path"); 28 29 if (path == null) { 30 path = ""; 31 } 32 33 setListAdapter(new SimpleAdapter(this, getData(path), 34 android.R.layout.simple_list_item_1, new String[] { "title" }, 35 new int[] { android.R.id.text1 })); 36 getListView().setTextFilterEnabled(true); 37 } 38 39 protected List<Map<String, Object>> getData(String prefix) { 40 List<Map<String, Object>> myData = new ArrayList<Map<String, Object>>(); 41 42 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 43 mainIntent.addCategory(Intent.CATEGORY_SAMPLE_CODE); 44 45 //通过PackageManager从AndroidManifest.xml中读取所有含有Intent.ACTION_MAIN 46 //和Intent.CATEGORY_SAMPLE_CODE的所有Activity信息 47 PackageManager pm = getPackageManager(); 48 List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0); 49 50 if (null == list) 51 return myData; 52 53 String[] prefixPath; 54 String prefixWithSlash = prefix; 55 56 if (prefix.equals("")) { 57 prefixPath = null; 58 } else { 59 prefixPath = prefix.split("/"); 60 prefixWithSlash = prefix + "/"; 61 } 62 63 //变量len 记录了满足条件的Activity个数 64 int len = list.size(); 65 66 Map<String, Boolean> entries = new HashMap<String, Boolean>(); 67 68 for (int i = 0; i < len; i++) { 69 ResolveInfo info = list.get(i);//获取对应activity的ResolveInfo信息 70 CharSequence labelSeq = info.loadLabel(pm);//获取对应activity的android:label属性值 71 72 //如果为空,则label初始化为activity对应的android:name属性值,否则直接转换为string赋值给label 73 String label = labelSeq != null 74 ? labelSeq.toString() 75 : info.activityInfo.name;//anctivityInfo.name对应于属性android:name 76 77 if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) { 78 79 String[] labelPath = label.split("/"); 80 81 //prefixPath为空时,nextLabel值为labelPath第一个字符串;否则为labelPath中索引为prefixPath长度的值,不会造成越界因为 82 //prefixPath的最大长度永远比labelPath最大长度小1 83 String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length]; 84 85 if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) { 86 addItem(myData, nextLabel, activityIntent( 87 info.activityInfo.applicationInfo.packageName, 88 info.activityInfo.name)); 89 } else { 90 if (entries.get(nextLabel) == null) { 91 addItem(myData, nextLabel, browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel)); 92 entries.put(nextLabel, true); 93 } 94 } 95 } 96 } 97 98 Collections.sort(myData, sDisplayNameComparator); 99 100 return myData; 101 } 102 103 private final static Comparator<Map<String, Object>> sDisplayNameComparator = 104 new Comparator<Map<String, Object>>() { 105 private final Collator collator = Collator.getInstance(); 106 107 public int compare(Map<String, Object> map1, Map<String, Object> map2) { 108 return collator.compare(map1.get("title"), map2.get("title")); 109 } 110 }; 111 112 //叶子列表项对应的Intent 113 protected Intent activityIntent(String pkg, String componentName) { 114 Intent result = new Intent(); 115 result.setClassName(pkg, componentName); 116 return result; 117 } 118 119 //分类列表项对应的Intent 120 protected Intent browseIntent(String path) { 121 Intent result = new Intent(); 122 result.setClass(this, ApiDemos.class); 123 result.putExtra("com.example.android.apis.Path", path); 124 return result; 125 } 126 127 protected void addItem(List<Map<String, Object>> data, String name, Intent intent) { 128 Map<String, Object> temp = new HashMap<String, Object>(); 129 temp.put("title", name); 130 temp.put("intent", intent); 131 data.add(temp); 132 } 133 134 @Override 135 protected void onListItemClick(ListView l, View v, int position, long id) { 136 Map<String, Object> map = (Map<String, Object>)l.getItemAtPosition(position); 137 138 Intent intent = (Intent) map.get("intent"); 139 startActivity(intent); 140 } 141 }
代码的103-109行自定义了一个比较器,用来对列表项进行排序。
注意:
ListActivity中的listView的每个Item都包含以下内容:
1、 title,显示出来的内容;
2、 intent,隐藏的内容,Intent类型主要有两种:
一种指向叶子列表项,点击后运行具体的Activity;
一种指向分类列表项,即点击后仍运行ApiDemos这个继承自ListActivity的Activity,只是此时该Activity对应的SimpleAdapter不一样了,因而类表显示的内容也不一样了。
可以在eclipse中调试运行,这样可以更快的理解具体的流程:
第一次获取数据源后(通过getData()获得),数据源的title项如下:
Accessibility
Animation
App
Content
Graphics
Media
NFC
OS
Preference
Text
Views
点击上面列出的项目后,如点击App后,将会启动App项隐藏的Intent 对应的Activity, 而该Activity仍为ApiDemos,所以进入之后就重新创建并初始化myData,其对应的内容为:
ActionBar
Activity
Alarm
…
点击上面列出的项目后,如点击Activity后,将会启动Activity项隐藏的Intent对应的Activity,而该Activity仍为ApiDemos,所以进入之后重新创建并初始化myData,其对应的内容为:
Animation
Custom Dialog
Custom Title
…
点击上面列出的项目后,如Custom Title后,将会启动Custom Title项隐藏的Intent对应的Activity,这是情况不一样了,此时的Activity对应的名称为ResolveInfo对象info中保存的名称为info.activityInfo.name的Activity,这是就可以看到效果!(此时就会呈现具体的界面)
我们看到的列表显示效果都是按字典序排列的,这是因为在结尾调用了自定义的比较器sDisplayNameComparator的缘故!
通过以上比较笨的步骤就能大致了解整个示例安排的结构了,噢耶!