之前一直在公司做设置模块的功能修改,现在转到了其它模块,所以就总结一下吧,有不足之处,还请指正.
设置是整个android系统的重要应用,涉及的都是系统功能,本文只是对其结构进行解析,功能控制等不做说明
(1)设置的界面选择
设置里面的Activity乍一看和常规应用的Activity有很大不同,但是原理都是一样的,不过设置为了更方便的区分和获取信息,在AndroidManifest.xml文件中添加了许多的属性,这可能导致很多人看着头疼,但是这也是设置的精髓所在.
设置的启动界面: O/packages/apps/Settings/src/com/android/settings/Settings.java
Settings的父类是SettingsActivity,而且其中有很多继承SettingsActivity的内部类,这个地方是很有意思,后续再说,先看下SettingsActivity.java
O/packages/apps/Settings/src/com/android/settings/SettingsActivity.java
SettingsActivity的父类是SettingsDrawerActivity,而SettingsDrawerActivity是在SettingLib中定义
O/frameworks/base/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
SettingsDrawerActivity名称是沿用N版本的名字,但是侧滑栏功能在O版本上已经移除了,google在这个地方偷了下懒.
这父子俩的onCreate():
SettingsDrawerActivity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//主要是布局设置
super.setContentView(R.layout.settings_with_drawer);
mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container);
Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
toolbar.setVisibility(View.GONE);
return;
}
setActionBar(toolbar);
}
SettingsActivity
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
......
//获取的类名,此处获取的可能是在Settings.java中的内部类,或者就是Settings.java
// Should happen before any call to getIntent()
getMetaData();
// Getting Intent properties can only be done after the super.onCreate(...)
final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
......
final ComponentName cn = intent.getComponent();
final String className = cn.getClassName();
//mIsShowingDashboard会根据不同的类名布置不同的布局
mIsShowingDashboard = className.equals(Settings.class.getName());
// This is a "Sub Settings" when:
// - this is a real SubSettings
// - or :settings:show_fragment_as_subsetting is passed to the Intent
final boolean isSubSettings = this instanceof SubSettings ||
intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
// If this is a sub settings, then apply the SubSettings Theme for the ActionBar content
// insets
//设置二级界面的主题
if (isSubSettings) {
setTheme(R.style.Theme_SubSettings);
}
//布局不同的布置,主要是为了区分一级界面
setContentView(mIsShowingDashboard ? R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
mContent = (ViewGroup) findViewById(R.id.main_content);
getFragmentManager().addOnBackStackChangedListener(this);
......
//跳转到制定的界面
launchSettingFragment(initialFragmentName, isSubSettings, intent);
......
}
在SettingsActivity的onCreate中去其实会有三个不同布局的加载方向,以下是重点了:
Settings , Settings的内部类 , SubSettings
(a)Settings ---主界面(一级界面)
onCreate函数中会根据类名加载不同的布局,而布尔值:mIsShowingDashboard 就是当前是否为一级界面的判断
mIsShowingDashboard = className.equals(Settings.class.getName());
setContentView(mIsShowingDashboard ?
R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
settings_main_dashboard.xml就很简单了只是一个FrameLayout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent"
/>
在之后的界面跳转即 launchSettingFragment(initialFragmentName, isSubSettings, intent)函数中又做出了区分:
void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
if (!mIsShowingDashboard && initialFragmentName != null) {
......
} else {
// No UP affordance if we are displaying the main Dashboard
mDisplayHomeAsUpEnabled = false;
// Show Search affordance
mDisplaySearch = true;
mInitialTitleResId = R.string.dashboard_title;
//此处是真正显示一级界面的操作
switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
mInitialTitleResId, mInitialTitle, false);
}
}
在launchSettingFragment函数中正式加载了设置的一级界面DashboardSummary.java,DashboardSummary是一个Fragment,而内部布局是使用的R.layout.dashboard,dashboard.xml包含了一个自定义的RecyclerView类FocusRecyclerView,此处就不再详细说明了.
(b)Settings的内部类
Settings的内部类的启动一版都是通过activity 中的action属性启动的,而判断的依据也是通过mIsShowingDashboard,加载的布局为R.layout.settings_main_prefs,而settings_main_prefs.xml比dashboard.xml来说就增加了一个switchBar:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_height="0px"
android:layout_width="match_parent"
android:layout_weight="1">
<com.android.settings.widget.SwitchBar android:id="@+id/switch_bar"
android:layout_height="?android:attr/actionBarSize"
android:layout_width="match_parent"
android:background="@drawable/switchbar_background"
android:theme="?attr/switchBarTheme"
/>
<FrameLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
<RelativeLayout /><!--没怎么研究过,此处代码就省略了-->
</LinearLayout>
Settings的内部类的主体显示内容依然是一个Fragment,而这个Fragment已经在AndroidManifest.xml中定义好了.拿StorageDashboardActivity为例:
<activity android:name=".Settings$StorageDashboardActivity"
android:label="@string/storage_settings"
android:icon="@drawable/ic_settings_storage"
android:taskAffinity="com.android.settings"
android:parentActivityName="Settings">
<intent-filter android:priority="1">
<action android:name="android.settings.INTERNAL_STORAGE_SETTINGS" />
<action android:name="android.settings.MEMORY_CARD_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.VOICE_LAUNCH" />
</intent-filter>
<intent-filter android:priority="5">
<action android:name="com.android.settings.action.SETTINGS" />
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.homepage" />
<meta-data android:name="com.android.settings.title"
android:resource="@string/storage_usb_settings" />
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.deviceinfo.StorageSettings" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
这个地方很容易会让人迷糊的,主要是里面的属性太多,看第一眼估计就会晕掉,这些属性是在SettingLib中的O/frameworks/base/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java有用到的,后续有时间的话可以总结一下,TileUtils主要目录加载的工具类.
言归正传,内部类StorageDashboardActivity的Fragment显示内容为:com.android.settings.FRAGMENT_CLASS,即com.android.settings.deviceinfo.StorageSettings
(c)SubSettings
和内部类就很相似了,不过是做了主题的切换:
if (isSubSettings) {
setTheme(R.style.Theme_SubSettings);
}
SubSettings的启动方式是很隐蔽的这里把启动的顺序贴下,各位有时间的话可以自己研究一下:
android.support.v7.preference.Preference$1.onClick()
android.support.v7.preference.Preference.performClick()
com.android.settings.fuelgauge.PowerUsageSummary.onPreferenceTreeClick()
com.android.settings.dashboard.DashboardFragment.onPreferenceTreeClick()
android.support.v14.preference.PreferenceFragment.onPreferenceTreeClick()
com.android.settings.SettingsActivity.onPreferenceStartFragment()
com.android.settings.SettingsActivity.startPreferencePanel()
com.android.settings.Utils.startWithFragment()
com.android.settings.Utils.onBuildStartFragmentIntent()
到这里为止,设置的界面加载启动基本上已经总结完了,看起来还是很简单的,但是这只是启动而已,内部的数据加载,数据与View的绑定其实还有很多东西,后续有时间的话会继续总结.