之前一直在公司做設置模塊的功能修改,現在轉到了其它模塊,所以就總結一下吧,有不足之處,還請指正.
設置是整個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的綁定其實還有很多東西,後續有時間的話會繼續總結.