Android Settings源码结构分析与自实现
来源:互联网 发布:兴财软件 编辑:程序博客网 时间:2024/06/06 03:42
最近的项目一直是按照PRD与高清,修改系统设置,调整布局、间距、颜色,涉及到一些流程的更改与自定义控件,以及对settings源码结构的研究。在项目相对空闲是,做个整理记录。由于项目依赖系统源码环境,而且在赶项目的时候,只能以最快的速度解决当前的问题,而下面的设计的代码与效果图,都是个人封装的DEMO测试,毕竟不能仅仅只是最求项目的解决过关,学过用过,就应该做点总结,毕竟我觉得很多东西,在赶项目的时候是无法去过多的仔细研究,所以有居多“废代码”,很多地方是值得仔细研究与优化改进的。
首先对原生Settings的布局,及切换跳转,按照我的研究理解,做个流程的分析简介,后面会给出我的改进与实现
1.先从布局简单的说起:
在PreferenceActivity中 可以看到:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(com.android.internal.R.layout.preference_list_content);
系统默认会加载这个布局文件,它是一个左右分屏的,左边是一个ListView,右边是一个android.preference.PreferenceFrameLayout,左边ListView 负责切换,右边显示相应的Fragment。可以到sdk目录下查看该布局文件(sdk\platforms\android-17\data\res\layout\preference_list_content)
2.右边ListView的显示。
代码详见内部类:HeaderAdapter。
//类型一,分类title,无焦点,不可点击 static final int HEADER_TYPE_CATEGORY= 0; //类型二,正常的可点击的header项 static final int HEADER_TYPE_NORMAL = 1; //带 switch 开关的header项 static final int HEADER_TYPE_SWITCH = 2;
三种类型,分别对应的布局:
case HEADER_TYPE_CATEGORY://下划线样式的TextView view = new TextView(getContext(), null,android.R.attr.listSeparatorTextViewStyle);
case HEADER_TYPE_SWITCH://含有switch 控件的布局 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,false);
case HEADER_TYPE_NORMAL: view = mInflater.inflate(R.layout.preference_header_item, parent, false);
说完布局,我们再来说说关于启动流程的问题:
getMetaData-->onBuildHeaders-->onGetInitialHeader-->super.switchToHeader-->highlightHeader
1.onCreat 方法:
//...省略部分getMetaData(); mInLocalHeaderSwitch = true; super.onCreate(savedInstanceState); mInLocalHeaderSwitch = false; highlightHeader(mTopLevelHeaderId);//... 省略部分
2.getMetaData:
主要作用是获取当前Activity的 meta信息,参看manifest的定义,如这个是wifi设置界面的Activity信息描述
<activity android:name="Settings$WifiSettingsActivity" android:label="@string/wifi_settings" android:configChanges="orientation|keyboardHidden|screenSize" android:clearTaskOnLaunch="true" android:parentActivityName="Settings"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="android.settings.WIFI_SETTINGS" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.VOICE_LAUNCH" /> <category android:name="com.android.settings.SHORTCUT" /> </intent-filter> <meta-data android:name="com.android.settings.FRAGMENT_CLASS" android:value="com.android.settings.wifi.WifiSettings" /> <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID" android:resource="@id/wifi_settings" /> </activity>
定义了,跳转进来的action,已经这个它要展示的信息,左边Header的 id(mTopLevelHeaderId),右边显示的fragment 类(mFragmentClass)。
下面是读取该信息。
private void getMetaData() { try { //获取当前Activity的Meta 信息 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); if (ai == null || ai.metaData == null) return; //ListView 中要选中的Header的Id 如R.id.wifi_settings mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); //对应的切换 右边显示的Fragment mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); // Check if it has a parent specified and create a Header object //这个 应该是针对 single panel 检查它是否有上一级。 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE); String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); if (parentFragmentClass != null) { mParentHeader = new Header(); mParentHeader.fragment = parentFragmentClass; if (parentHeaderTitleRes != 0) { mParentHeader.title = getResources().getString(parentHeaderTitleRes); } } } catch (NameNotFoundException nnfe) { // No recovery } }
3.super.onCreate(savedInstanceState);
PreferenceActivity 源码 onCreat 方法主要 调用如下,
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);//布局 setContentView(com.android.internal.R.layout.preference_list_content);//...省略部分代码 if (initialFragment != null && mSinglePane) { //单屏 } else { onBuildHeaders(mHeaders); if (mHeaders.size() > 0) { if (!mSinglePane) { if (initialFragment == null) { Header h = onGetInitialHeader(); switchToHeader(h); } else { switchToHeader(initialFragment, initialArguments); } } } }
在PreferenceActivity 的onCreat 中 依次调用 onBuildHeaders-->onGetInitialHeader
onBuildHeaders:加载header资源,显示在右边的ListView。
onGetInitialHeader:初始化的时候,首显项。
4.Settings 类对 onBuildHeaders的重写:
@Override public void onBuildHeaders(List<Header> headers) { //Header 资源 loadHeadersFromResource(R.xml.settings_headers, headers); //根据相应条件,移除掉部分header,筛选出mFirstHeader //第一个不为HEADER_TYPE_CATEGORY(不能获得触摸焦点) 分类项的Header,作为备用显示 //并记录 header id 对应ListView中的 index updateHeaderList(headers); }
5.Settings 类对 onGetInitialHeader的重写:
@Override public Header onGetInitialHeader() { //获取 要显示的fragment String fragmentClass = getStartingFragmentClass(super.getIntent()); //构造 header 对象 if (fragmentClass != null) { Header header = new Header(); header.fragment = fragmentClass; header.title = getTitle(); header.fragmentArguments = getIntent().getExtras(); mCurrentHeader = header; return header; } //如果meta中没有,intent中也没有,返回updateHeaderList 中选出来的header return mFirstHeader; } protected String getStartingFragmentClass(Intent intent) { //如果前面 getMeta 中的读取到的mFragmentClass 不为空,直接return if (mFragmentClass != null) return mFragmentClass; //获取intent 中指定要 跳转到的类名 String intentClass = intent.getComponent().getClassName(); //就是当前activity ,不做处理 if (intentClass.equals(getClass().getName())) return null; if ("com.android.settings.ManageApplications".equals(intentClass) || "com.android.settings.RunningServices".equals(intentClass) || "com.android.settings.applications.StorageUse".equals(intentClass)) { // Old names of manage apps. intentClass = com.android.settings.applications.ManageApplications.class.getName(); } return intentClass; }
6.PreferenceActivity 的onCreate中 switchToHeader ,就显示 了指定的fragment和 高亮指定的header项。
7.Settings 的onCreate中 ,更新左边的选中项,mTopLevelHeaderId 也是从getMetaData 中读出来的。因为在PreferenceActivity 的switchToHeader 中,如果ListView没有找到Header,就不会有高亮的选中项。
highlightHeader(mTopLevelHeaderId);
至此,Settings 的加载显示,就已经完成了,同时也展示了manifest中Activity的 meta-data的用法。
在此处,也就可以解释为何Settings类中有着那么多空实现的 public static 的内部类了,这可以说是一种Template 模式,使得Settings中的功能模块对外使用更加灵活,Settings主类描述了具体的算法架构,而不同的内部类通过manifest 中声明的meta-data,可以有不同的界面内容显示。如果说activity 可以通过 activity-alias 来描述一个多入口,以个性化的显示不同的信息,那么那些空实现的静态内部类,也是帮Settings实现这样的效果。一个有效的Intent 请求过来,匹配对应的内部类Activity,然后读取meta-data数据显示,从而“一步到位”的跳到想要的Settings页面,而不用一步步的点击切入。因为Fragment无法在manifest中进行描述声明,为其指定拦截的action,所以只能将其宿主到Activity中。
以上就是我对Settings启动流程的分析研究,有什么疑问或者不同的见解都可以交流交流。
- Android Settings源码结构分析与自实现
- Android Settings源码结构分析与自实现
- 20150623----Android-Settings源码分析
- Android Settings源码流程分析
- Android原生Settings源码分析
- Android Settings和SettingsProvider源码分析与修改
- Android Settings和SettingsProvider源码分析与修改
- (OK) Android Settings和SettingsProvider源码分析与修改
- android开发中Settings结构简单分析
- Android L Settings界面结构简单分析
- Android Settings(系统设置)源码分析(一)
- Android 5.1 Settings源码简要分析
- Android 5.1 Settings源码简要分析
- Android 5.1settings源码简要分析
- Android Settings(系统设置)源码分析(一)
- Android 5.1 Settings模块源码分析
- Android 5.1 Settings源码简要分析
- Android源码结构分析
- wikioi-天梯-通过初赛-最小生成树-1078:最小生成树
- 五、java面向对象编程(一)——类与对象
- 感谢两位神人
- C++new失败的处理
- 今日面试问题总结
- Android Settings源码结构分析与自实现
- Ultra Librarian
- ssh localhost 无密钥配置(root & !root)
- java语言环境的搭建
- A+B for Input-Output Practice (VII)
- 第一章 概述 1.8客户—服务器模型
- tomcat+apache负载均衡集群
- JS高级应用之数组基础
- 学习图像处理之前景检测一 初识ViBe