六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 103|回复: 0

Android API Demo框架分析

[复制链接]

升级  32%

4

主题

4

主题

4

主题

童生

Rank: 1

积分
16
 楼主| 发表于 2013-1-30 04:02:06 | 显示全部楼层 |阅读模式
 闲着没事儿,看了下Android API Demo的代码,发下他的框架写的挺有意思,特记录在这里,以便将来查阅。
 
I、整体的思路
 
熟悉或看过系统自带的API Demo的人,应该都有印象,根据各层demo的类别分组,每个组里面包包含示例activity,如下图:
 
 
   
 
如果要是手动实现这个效果,无非有两种方法:
1、前两层都创建一个Activity,每个Activity中都包含一个ListView, 在底层关联真正的demo代码
2、前两层都用ListActivity,在底层关联真正的demo代码
 
 前两层的本质都是一样的,用ListView实现,这样做有一个麻烦的地方,就是需要创建很多个Activity(具体的个数视分组的个数而定),同样的
代码需要copy几次,麻烦。。
 
那么,有什么简便方法吗,看看API Demo,他是怎么做的?
1、如上面所说的使用 ListActivity(这个,跟咱们一般的想法一致)
2、用一个ListActivity实现分组或直接调用最终的demo代码
实现的原理就是在intent中筛选自己定义的intent组、保存到一个list中
 
 
II、API Demo的实现方式
 
下面是测试Demo从API Demo中摘出的代码,咱们分析下:
 
 
public class testActivity extends ListActivity{    // 定义用于检索自己定义的Activity的Category的名称    public static final String CATEGORY_MYAPP_DEMO = "android.intent.category.MY_DEMO";    // 定义用于保存路径信息的键    public static final String PKG_PATH = "com.vs.apidemo.Path";        String TAG = "AUTO_TEST";        @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);                // 获取保存的路径信息        Intent intent = getIntent();        String path = intent.getStringExtra(PKG_PATH);                if (path == null)        {            path = "";        }                // 设置ListActivity对应的List数据        setListAdapter(new SimpleAdapter(this, getData(path), android.R.layout.simple_list_item_1,            new String[] {"title"}, new int[] {android.R.id.text1}));                // 设置ListView可以根据用户输入的字串自动检索(当ListView获取焦点时)        getListView().setTextFilterEnabled(true);                Toast.makeText(this, "path=" + path, Toast.LENGTH_SHORT).show();    }        @Override    protected void onListItemClick(ListView l, View v, int position, long id)    {        // 获取map中保存的intent        Map map = (Map)l.getItemAtPosition(position);                // 启动目标intent        Intent intent = (Intent)map.get("intent");        startActivity(intent);    }        protected List getData(String prefix)    {        List<Map> myData = new ArrayList<Map>();                // 创建指定Action类型的Intent        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);        // 添加到自己定义的Category中        mainIntent.addCategory(CATEGORY_MYAPP_DEMO);                // 获取所有自己定义的Category的Activity信息,并保存到list中        PackageManager pm = getPackageManager();        List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);                // 如果没有符合条件的Activity,直接返回        if (null == list)            return myData;                // 定义保存路径的变量        String[] prefixPath;                if (prefix.equals(""))        {            prefixPath = null;        }        else        {            // 将当前AA/B/C格式的路径转换为String数组{{AA},{B},{C}}            prefixPath = prefix.split("/");        }                // 获取满足条件的Activity的数目        int len = list.size();                Log.e(TAG, "list.size=" + list.size()            + "\nlist:" + list.toString());                Map<String, Boolean> entries = new HashMap<String, Boolean>();                for (int i = 0; i < len; i++)        {            ResolveInfo info = list.get(i);            /*             *  获取Activity的label,此label即是在Manifest中,用“android:label="@string/activity_dialog"”             *  定义的Activity label属性,具体参考Manifest文件             *  label定义的是此Activity的路径             */            CharSequence labelSeq = info.loadLabel(pm);                        // 如果没有定义label值,则获取Activity定义的名称,即“android:name=”定义的字串            String label = labelSeq != null ?                 labelSeq.toString() : info.activityInfo.name;                                    if (prefix.length() == 0 || label.startsWith(prefix))            {                                // 将路径名称转换为数组                String[] labelPath = label.split("/");                                // 获取下层目录的名称                String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length];                                                // 如果顶层是测试Activity或最下一层的Activity时,设置启动的Activity为测试Activity                if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1)                {                    // 以Activty的label为键,将对应的启动Activity的Intent保存到list中                    addItem(myData,                        nextLabel,                        activityIntent(info.activityInfo.applicationInfo.packageName,                             info.activityInfo.name));                }                else                {                    // 如果还是目录,且该目录没有保存过,则保存                    if (entries.get(nextLabel) == null)                    {                        /*                         *  以目录为键,将启动[本]Activity的Intent保存到list中                         *  注意了,这里是本Activity,所有的目录嵌套的迁移实际上都是复用一个Activity                         *  正是这个处理,简化了代码~~                         */                        addItem(myData, nextLabel, browseIntent(prefix.equals("") ?                             nextLabel : prefix + "/"                            + nextLabel));                        entries.put(nextLabel, true);                    }                }            }        }                // 排序        Collections.sort(myData, sDisplayNameComparator);                return myData;    }        /**     * 使用Collator对字串进行排序     */    private final static Comparator<Map> sDisplayNameComparator = new Comparator<Map>()    {        private final Collator collator = Collator.getInstance();                public int compare(Map map1, Map map2)        {            return collator.compare(map1.get("title"), map2.get("title"));        }    };        /**     * <创建启动Demo Activity用的Intent>     * @param pkg 包名     * @param componentName Activity的name,即Manifest里面name指定的名称     */    protected Intent activityIntent(String pkg, String componentName)    {        Intent result = new Intent();        result.setClassName(pkg, componentName);        return result;    }        /**     * <创建启动本Activity用的Intent>     * @param path 路径     */    protected Intent browseIntent(String path)    {        Intent result = new Intent();        // 启动本Activity用的Intent        result.setClass(this, testActivity.class);        // 将路径传递给下层目录,即在onCreate需要取出的数据        result.putExtra(PKG_PATH, path);        return result;    }        /**     * <将所有路径下对应启动Activity要用的Intent保存到list中>     * <功能详细描述>     * @param data 保存的list     * @param name 键     * @param intent 启动Activity用的Intent     */    protected void addItem(List<Map> data, String name, Intent intent)    {        Map<String, Object> temp = new HashMap<String, Object>();        temp.put("title", name);        temp.put("intent", intent);        data.add(temp);    }}           
通过上面的代码,可以看出,复用是通过如下几步完成:
1、先根据Category筛选出自己定义的Activity
2、再根据Activity的label解析路径
3、然后根据路径判断,如果是最后一层,则将启动测试demo的Intent保存到Map中,如果不是,则重复调用本Activity
 
思路很像递归。
 
上面提到label,咱们看看它在的Manifest的定义吧:
 
<application android:icon="@drawable/icon" android:label="@string/app_name"><!--默认启动Activity-->   <activity android:name=".app.testActivity" android:label="@string/app_name">          <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />          </intent-filter>    </activity><!--Demo Activity 注意:1、label定义字串必须是他的实际路径2、必须指定Activity的action为android.intent.action.MAIN3、必须指定Activity的Category为android.intent.category.MY_DEMO这个与代码中定义的一致要是1不满足,显示的可能就不是想要的分组了要是2、3不满足,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.MY_DEMO" />            </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.MY_DEMO" />            </intent-filter>     </activity></application> 
  下面是label字串的定义(strings.xml中):
<string name="activity_custom_dialog">App/Activity/Custom Dialog</string><string name="activity_dialog">App/Activity/Dialog</string> 
嗯,差不多就这样了~~
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表