Android系统源码剖析(一)---Settings
来源:互联网 发布:知金教育好吗 编辑:程序博客网 时间:2024/05/17 02:12
本文为博主辛苦总结,针对Android4.42源码分析,转载请注明出处,http://blog.csdn.net/zrf1335348191/article/details/50837027
最近在研究Android的Settings源码,先看一下源码的目录结构。大概967左右个文件,是不是及其头疼而且无从下手?待我娓娓道来~~~~~
1,初识Settings
首先,这么多文件,到底哪个文件是主界面呢?在Settings目录下找到Androidmanifest.xml清单配置文件,找到首先启动的activity:
<activity android:name="Settings" android:label="@string/settings_label_launcher" android:taskAffinity="com.android.settings" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="android.settings.SETTINGS" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
可以看到,设置的主界面是Settings.Java(package com.android.settings;),
public class Settings extends PreferenceActivity implements ButtonBarHandler, OnAccountsUpdateListener { .....<pre name="code" class="java"> loadHeadersFromResource(R.xml.settings_headers, headers);//加载布局 .....}
所对应的xml文件为Settings_headers.xml(res\xml\)文件。在此摘列出xml文件的一部分。
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <!-- WIRELESS and NETWORKS --> <header android:id="@+id/wireless_section" android:title="@string/header_category_wireless_networks" /> <!-- Wifi --> <header android:id="@+id/wifi_settings" android:fragment="com.android.settings.wifi.WifiSettings" android:title="@string/wifi_settings_title" android:icon="@drawable/ic_settings_wireless" /> <!-- MobileData --> <header android:id="@+id/mobiledata_settings" android:icon="@drawable/stat_notify_mobile_data" android:title="@string/data_usage_enable_mobile"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.phone" android:targetClass="com.android.phone.MobileNetworkSettings" /> </header>.........</preference-headers>
每个可以选择和点击的item基本有四个属性,以WiFi_header为例
id:对应的id
fragment:点击之后的fragment:WifiSettings
title:header的主标题,即在Settings主界面显示的文本:WLAN
icon:header的图标,即显示在文本左侧的图标
分析这两个文件可以总结下Settings的布局,Settings主界面显示借助PreferenceActivity,Preference意为偏爱偏好,特点是利用键值对记录用户上次的选择,在下次进入到该界面时直接读取上次的选择无须再进行配置。Activity意为界面,preferenceactivity结合两者。每行属于一个header,相当于listview中的item,每一个header又有fragment与之对应,而fragment的加载依赖于Activity,所依赖的Activity为SubSettings.java(package com.android.settings;//继承与Settings),在Subsetting.java中已经写明:
/** *Stub class for showing sub-settings; we can't use the main Settings class * since for our app it is a special singleTask class. * 不能直接使用Settings.java加载fragment,因为,我们的程序启动模式是singleTask */public class SubSettings extends Settings { @Override public boolean onNavigateUp() { finish(); return true; } @Override protected boolean isValidFragment(String fragmentName) { return true; }}
对Setting源码的分析可以分两个步骤进行入手,
第一,headers列表的加载
第二,header的点击事件的处理
解决以上两个问题后,就可以开始对不同模块进行分析
2,设置界面布局,加载headers
(1),加载xml布局文件
可以使用两种方式加载xml文件布局
方法一:
loadHeadersFromResource(R.xml.settings_headers, headers);
方法二:
addPreferencesFromResource(R.xml.fragmented_preferences_inner);
(2),定义adapter加载并显示headers
private static class HeaderAdapter extends ArrayAdapter<Header> {
设置界面布局的适配器adapter,有以下几种type
i>,HEADER_TYPE_CATEGORY:无焦点,不可以点击
ii>,HEADER_TYPE_BUTTON:带有button的header,button的visibility(可见性)有条件(可自行设置)
iii>,HEADER_TYPE_NORMAL:正常的可获取焦点可点击的不带button的header
3,Settings.java源码分析(部分提取)
(1),onCreate方法中:
if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) { getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0)); }
以上这段代码用于布局actionbar,即顶部的导航栏布局,如果获取到的intent中的数值为
ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW,即表示,当屏幕较窄时导航栏有一部分会显示在底部。
mAuthenticatorHelper = new AuthenticatorHelper(); mAuthenticatorHelper.updateAuthDescriptions(this); mAuthenticatorHelper.onAccountsUpdated(this, null);
这段代码属于配置一些认证或者更新账户信息,一般不做修改
getMetaData();查看方法源码可以看到:方法是获取到配置文件Androidmanifest.xml中<meta-data.../>节点下的数据
private void getMetaData() { try { //获取到配置文件Androidmanifest.xml文件中<meta-data.../>节点下的数据 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); //如果没有信息,则返回 if (ai == null || ai.metaData == null) return; //获取到header所对应的id mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); //获取到header所对应的fragment文件 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); // Check if it has a parent specified and create a Header object //检查一下是否有parent,若有,就创建出来 //parent的title final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE); //parent的fragment 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 } }
if (!onIsHidingHeaders() && onIsMultiPane()) { highlightHeader(mTopLevelHeaderId); // Force the title so that it doesn't get overridden by a direct launch of // a specific settings screen. setTitle(R.string.settings_label); }
onIsMultiPane()判断是否双屏幕MultiPane,平板双屏显示,手机一般单屏SinglePane显示,所以onIsMultiPane()方法可以设置为返回false。
onIsHidingHeaders判断是否是双屏的headers均有显示。
如果满足条件就利用highlightHeader()方法标亮所选择的header进行区别于其他headers,并且将导航栏title定为设置,保证不被覆盖。
if (onIsMultiPane()) { //导航栏左上角图标的左边是否显示返回图标,false表示不显示 getActionBar().setDisplayHomeAsUpEnabled(false); //导航栏左上角图标是否可点击,false代表不可点击 getActionBar().setHomeButtonEnabled(false); //导航栏左上角的图标是否显示 getActionBar().setDisplayShowHomeEnabled(true) }以上代码是说如果是多屏显示,则对导航栏左上角程序图标以及返回图标的设置
接下来是利用savedInstanceState恢复数据的操作,不再贴出
showBreadCrumbs(mCurrentHeader.title, null);
设置当前header的标题显示
if (mParentHeader != null) { setParentTitle(mParentHeader.title, null, new OnClickListener() { @Override public void onClick(View v) { 。。。。。。 } }); }
设置parentheader的标题title以及设置title的点击事件。
(2),onresume方法,显示出来所有的header,借助于headerAdapter.resume()方法显示
header即item需要显示什么类型的布局可以在该adapter中进行修改,针对不同的item配置不同的布局文件
private static class HeaderAdapter extends ArrayAdapter<Header> {static int getHeaderType(Header header) { .........} public View getView(int position, View convertView, ViewGroup parent) { ..........}}
(3),onBuildStartFragmentIntent方法
@Override public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, int titleRes, int shortTitleRes) { Intent intent = super.onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes); // Some fragments want split ActionBar; these should stay in sync with // uiOptions for fragments also defined as activities in manifest. //有些header所对应的fragment会将信息同步更新到window即状态栏 if (WifiSettings.class.getName().equals(fragmentName) || WifiP2pSettings.class.getName().equals(fragmentName) || BluetoothSettings.class.getName().equals(fragmentName) ||....) { //将想要更新的信息传递给fragment对应的activity,在这里是SubSettings intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW); } intent.setClass(this, SubSettings.class); return intent; }(4),onBuildHeaders方法,用来布局,以及更新headers,在PreferenceActivity的oncreate()方法中被调用,以及onGetInitialHeader()方法,也是在PreferenceActivity的oncreate方法中被调用
@Override public void onBuildHeaders(List<Header> headers) { if (!onIsHidingHeaders()) { loadHeadersFromResource(R.xml.settings_headers, headers); //该方法用于判定某些特定的header是否显示, //比如若本机无蓝牙模块则不显示蓝牙的header updateHeaderList(headers); } }
(5)doValidCheck(),以及isValidFragment 用来检查fragment是否有效,为适配Android4.4以下版本,保证不出异常
(6)onNewIntent:activity启动模式为singletask单任务模式,如果在战中存在activity的实例,当再次通过intent调起时不会再去oncreate创建实例,而是onNewIntent去重用该实例
@Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); // If it is not launched from history, then reset to top-level if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) { switchToHeaderLocal(mFirstHeader); } getListView().setSelectionFromTop(0, 0); } }
(7)Settings.java中的内部类,Settings.java中有好多实现的内部类
。。。。。。 public static class SecuritySettingsActivity extends Settings { /* empty */ } public static class LocationSettingsActivity extends Settings { /* empty */ }。。。。。。。。
这些内部类是为了加载那些fragment,作为fragment的宿主,可以从Androidmanifest.xml中看到,从其他快捷方式进入某个单独的设置模块时借助这些内部类来加载。比如可以创建蓝牙快捷方式,以及状态栏进入蓝牙时需要借助这些内部类来加载那些fragment。
<activity android:name="Settings$WirelessSettingsActivity" android:taskAffinity="com.android.settings" android:label="@string/wireless_networks_settings_title" android:parentActivityName="Settings">。。。。。。</activity>
4,自定义操作
明白Settings界面的布局原理后我们就可以随意的对Settings主界面的布局进行增删改了,对应的是header的修改
(1),修改header:在xml文件下找到想要修改的header对应的节点,文本,文本左侧图标,以及点击进入的fragment进行相应修改即可
(2),增加header:例如我要增加一项"权限管理",做法如下:
i>,在Settings.headers.xml文件中增加一个header节点:
<header android:id="@+id/authority_management android:fragment="com.android.settings.AuthorityManagementSettings" android:icon="@drawable/ic_settings_authority" android:title="@string/authority_settings"/>
ii>,新建一个fragment,AuthorityManagementSettings类
public class DeviceInfoSettings extends RestrictedSettingsFragment { ........ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.authority_management_settings); .........}}
- Android系统源码剖析(一)---Settings
- Android Settings(系统设置)源码分析(一)
- Android Settings(系统设置)源码分析(一)
- Android7.0 Settings 源码剖析一——Settings概括
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android系统架构剖析(一)
- android系统浏览器源码剖析
- Android Settings剖析与修改
- Android Settings剖析与修改
- android M Settings(系统设置)源码分析 设置的初始化过程
- eoe android客户端源码剖析(一)动画启动界面
- 从源码角度剖析Android事件分发机制(一)
- Java文本转语音
- Sicily 1488 矩阵取数游戏
- Actionbar change Height
- 热烈祝贺阿尔法Go首战告捷
- 深入浅出RxJava(二:操作符)
- Android系统源码剖析(一)---Settings
- @RequestMapping 用法详解之地址映射(转)
- Win10系统安装软件为了对电脑进行保护,已经阻止此应用解决方法
- 第二周项目1-宣告"主权"
- mobile
- 【bzoj3223】Tyvj 1729 文艺平衡树
- iOS开发--3D Touch使用代码实例
- c++服务器开发 之一 概要介绍
- ora-02064不支持分布式操作与Oracle自治事务