本問(wèn)將回答以下八個(gè)問(wèn)題,如有錯(cuò)誤,敬請(qǐng)批評(píng)指正,不勝感激!(注:本文中的Settings解析基于android4.0+)
問(wèn)題一、Settings的主界面是怎么實(shí)現(xiàn)的?
問(wèn)題二、為什么使用hierarchyviewer 時(shí)Settings中的很多界面顯示的都是SubSettings?
問(wèn)題三、hierarchyviewer 中顯示SubSetting時(shí)如何確定我進(jìn)入的是哪個(gè)fragment?
問(wèn)題四、點(diǎn)擊設(shè)置界面的某一個(gè)header時(shí),設(shè)置界面是如何切換的?
問(wèn)題五、Settings.java中g(shù)etMetaData與getStartingFragmentClass這兩個(gè)函數(shù)是否有點(diǎn)矛盾?
問(wèn)題六、Settings的shortcut是如何創(chuàng)建的?從shortcut進(jìn)入Settings的流程是什么?
問(wèn)題七、為什么我從Settings的shortcut進(jìn)入時(shí),hierarchyviewer顯示的就不是SubSettings(如Data usage)?
問(wèn)題八、Settings.java中很多繼承自它的內(nèi)部類(lèi)都是空實(shí)現(xiàn),為什么要寫(xiě)這些類(lèi)?
-----------------------------------------------------------------------------------------------------------------------------------------
由于項(xiàng)目需要,本人就對(duì)Android中的Settings進(jìn)行了解析,希望能幫到對(duì)Settings有興趣的同志們~
-----------------------------------------------------------------------------------------------------------------------------------------
問(wèn)題一、Settings的主界面是怎么實(shí)現(xiàn)的?
為了能適應(yīng)平板和手機(jī),Settings采用了PreferenceActivity和PreferenceFragment結(jié)合的實(shí)現(xiàn)方式。
Settings.java繼承自PreferenceActivity,是Settings的主界面,它通過(guò)loadHeadersFromResource函數(shù)(api level 11)加載res/xml/settings_headers.xml來(lái)構(gòu)造界面。在settings_headers.xml中聲明了要在Settings主界面顯示的各個(gè)header(如Sound、Display等)。Settings.HeaderAdapter將其中的header分為三類(lèi)。在Settings.HeaderAdapter中的getView方法中根據(jù)header的類(lèi)型使用不同的布局文件。
為header劃分類(lèi)型的函數(shù)
static int getHeaderType(Header header) {
if (header.fragment == null && header.intent == null) {
return HEADER_TYPE_CATEGORY; // 因?yàn)闆](méi)有指明fragment和intent
} else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings || header.id == R.id.mobiledata_settings) {
return HEADER_TYPE_SWITCH; // 針對(duì)特定的三個(gè)header,分別為Wi-Fi、Bluetooth和Mobile data
} else {
return HEADER_TYPE_NORMAL;
}
}
當(dāng)我們點(diǎn)擊主界面的header后會(huì)顯示與該header相關(guān)的設(shè)置界面。大部分(如Display的詳細(xì)設(shè)置界面)都是通過(guò)繼承PreferenceFragment來(lái)實(shí)現(xiàn)的;有一部分是在settings_headers.xml中聲明<intent>,當(dāng)被點(diǎn)擊時(shí)(觸發(fā)PreferenceActivity的onHeaderClick())將會(huì)通過(guò)startActivity來(lái)啟動(dòng)在<intent>節(jié)點(diǎn)中聲明的targetClass(如設(shè)置中的Add account)。
問(wèn)題二、為什么使用hierarchyviewer 時(shí)Settings中的很多界面顯示的都是SubSettings?
要解決這個(gè)問(wèn)題我們先要清楚為什么會(huì)寫(xiě)一個(gè)SubSettings.java繼承自Settings.java?
SubSettings.java中的注釋很清楚的告訴了我們?cè)颍?/span>
Stub class for showing sub-settings; we can't use the main Settings class since for our app it is a special singleTask class。
原來(lái)是因?yàn)镾ettings.java在聲明時(shí)指定了android:launchMode="singleTask"。
我們知道,要顯示Fragment的內(nèi)容,我們就需要為其指定一個(gè)Activity。而Settings中的很多設(shè)置界面是由PreferenceFragment來(lái)完成的,當(dāng)然也需要我們指定Activity.
onBuildStartFragmentIntent函數(shù)會(huì)為我們構(gòu)造一個(gè)顯示Fragment的Intent對(duì)象(該函數(shù)的注釋寫(xiě)的非常明白).Settings.java重寫(xiě)了這個(gè)函數(shù)(注,重寫(xiě)時(shí)它調(diào)用了super的該方法),在為intent對(duì)象setClass時(shí)都使用SubSettings.java.(注:在settings_headers.xml指定了intent的header是不會(huì)觸發(fā)onBuildStartFragmentIntent的)
結(jié)果就是,Settings中大部分fragment都是使用的SubSettings這個(gè)Activity來(lái)顯示。由于hierarchyviewer只是顯示當(dāng)前界面使用的Activity(不能顯示這個(gè)界面是由哪個(gè)Fragment構(gòu)造的),所以我們使用hierarchyviewer 對(duì)Settings進(jìn)行觀(guān)察時(shí)很多設(shè)置界面顯示的是SubSettings。
問(wèn)題三、hierarchyviewer 中顯示SubSetting時(shí)如何確定我進(jìn)入的是哪個(gè)fragment?
在res/xml/settings_headers.xml中聲明了各個(gè)header被點(diǎn)擊后使用的fragment。我們可以根據(jù)這個(gè)文件確定我們進(jìn)入的fragment。
例如,當(dāng)我們點(diǎn)擊Display時(shí)hierarchyviewer 中顯示SubSetting。我們通過(guò)查找settings_headers就可知道使用的是哪個(gè)fragment。
Display這個(gè)header在settings_headers.xml中的聲明:
<!-- Display -->
<header
android:id="@+id/display_settings"
android:icon="@drawable/ic_settings_display"
android:fragment="com.android.settings.DisplaySettings"
android:title="@string/display_settings" />
header中使用 android:fragment指明使用的fragment。由此可知,Display使用的是com.android.settings.DisplaySettings這個(gè)fragment
問(wèn)題四、點(diǎn)擊設(shè)置界面的某一個(gè)header時(shí),設(shè)置界面是如何切換的?
點(diǎn)擊設(shè)置界面的header時(shí),會(huì)觸發(fā)Settings中onHeaderClick函數(shù),主要的處理都在其父類(lèi)PreferenceActivity的onHeaderClick中實(shí)現(xiàn)的。如果這個(gè)header指定了fragment,在mSinglePane(標(biāo)識(shí)是否為小屏幕設(shè)備。如,區(qū)分手機(jī)還是平板)為true時(shí),會(huì)調(diào)用startWithFragment方法,在startWithFragment方法中將調(diào)用onBuildStartFragmentIntent方法來(lái)構(gòu)造intent對(duì)象(重要),最后使用該intent對(duì)象啟動(dòng)一個(gè)activity來(lái)顯示fragment。
以點(diǎn)擊Settings中的Display為例.
(Bluetooth同理,只不過(guò)啟動(dòng)的Activity變?yōu)锽luetoothSettingsActivity(繼承自Settings,但是沒(méi)有實(shí)現(xiàn)重寫(xiě)任何方法,所以與SubSettings是一樣的處理),fragment變?yōu)?com.android.settings.bluetooth.BluetoothSettings)
fragment是com.android.settings.DisplaySettings,activity是com.android.settings.SubSettings.(fragment是由onHeaderClick函數(shù)傳入的,activity是由onBuildStartFragmentIntent()指定的)
執(zhí)行startActivity后將啟動(dòng)SubSettings.java。即我們將會(huì)再一次執(zhí)行SubSettings和PreferenceActivity的onCreate方法(因?yàn)镾ettings.java的onCreate方法調(diào)用了super.onCreate()),但是這次并不會(huì)進(jìn)入Settings的主界面,因?yàn)槲覀兊氖褂玫膇ntent對(duì)象是有很大不同的。這一次onCreate函數(shù)(PreferenceActivity)中的initialFragment 將被初始化為com.android.settings.DisplaySettings,然后我們將進(jìn)入switchToHeader(),最后switchToHeaderInner會(huì)取得FragmentTransaction對(duì)象,然后執(zhí)行了transaction.replace(com.android.internal.R.id.prefs, f).就這樣把我們的fragment顯示出來(lái)了。在onCreate中會(huì)對(duì)其他view的visibility進(jìn)行設(shè)置,以保證只顯示prefs。如,將com.android.internal.R.id.headers的visibility設(shè)置為VIEW.GONE.
注:PreferenceActivity的布局文件為preference_list_content.xml
問(wèn)題五、Settings.java中g(shù)etMetaData與getStartingFragmentClass這兩個(gè)函數(shù)是否有點(diǎn)矛盾?
這兩個(gè)函數(shù)可以說(shuō)是相輔相成的(好官方,^_^)。getMetaData會(huì)從AndroidManifest.xml中讀取Activity的<meta-data>節(jié)點(diǎn)的數(shù)據(jù);getStartingFragmentClass則從啟動(dòng)Activity的intent中讀取數(shù)據(jù)。這兩個(gè)函數(shù)會(huì)對(duì)讀取到的數(shù)據(jù)進(jìn)行整合,getStartingFragmentClass依賴(lài)于getMetaData讀取到的數(shù)據(jù),但是它也可能對(duì)數(shù)據(jù)作出修改(為了兼容*,如對(duì)原有manage apps類(lèi)進(jìn)行特殊處理)。
問(wèn)題六、Settings的shortcut是如何創(chuàng)建的?從shortcut進(jìn)入Settings的流程是什么?
創(chuàng)建Settings的shortcut時(shí)Luancher將會(huì)啟動(dòng)CreateShortcut,創(chuàng)建shortcut所需的intent對(duì)象將會(huì)由CreateShortcut和其父類(lèi)LuancherActivity共同構(gòu)建(詳見(jiàn) CreateShortcut的onListItemClick),這時(shí)創(chuàng)建的Intent對(duì)象使用的就不是SubSettings了(LuancherActivity中intentForPosition函數(shù)執(zhí)行setClassName()時(shí)使用的參數(shù)并不是SubSettings).
CreateShortcut中列出了可以創(chuàng)建shortcut的設(shè)置項(xiàng),這些設(shè)置項(xiàng)怎樣檢索出來(lái)的?
原來(lái),在創(chuàng)建LuancherActivity的ActivityAdapter對(duì)象時(shí),其構(gòu)造函數(shù)中執(zhí)行了makeListItems函數(shù),該函數(shù)將使用PackageManager的queryIntentActivities來(lái)根據(jù)intent對(duì)象查詢(xún)符合條件的activity。使用的intent是從getTargetIntent函數(shù)返回的。不難發(fā)現(xiàn),要想在CreateShortcut中顯示,Activity在必須要有
<category android:name="com.android.settings.SHORTCUT" />
如果我們想將Security設(shè)置項(xiàng)添加到shortcut列表,我們只需要在androidmanifest.xml中聲明Settings$SecuritySettingsActivity部分加上
<category android:name="com.android.settings.SHORTCUT" />
即可。
回到正題,點(diǎn)擊shortcut進(jìn)入Settings時(shí),傳入的Intent對(duì)象中包含了目標(biāo)fragment和目標(biāo)activity以及其他信息。PreferenceActivity得到了足夠多的信息,因此在onCreate中將依次執(zhí)行switchToHeader()->setSelectedHeader(null)->switchToHeaderInner()->transaction.replace(com.android.internal.R.id.prefs, f);
這樣就完成了fragment的顯示(使用的activity是從intent解析出來(lái)的.在switchToHeaderInner中執(zhí)行Fragment.instantiate時(shí)使用的Context是this!!)。不像執(zhí)行onHeaderClick那樣會(huì)執(zhí)行函數(shù)onBuildStartFragmentIntent(Settings中重寫(xiě)了該函數(shù))來(lái)重新指定我們使用的Activity。
問(wèn)題七、為什么我從Settings的shortcut進(jìn)入時(shí),hierarchyviewer顯示的就不是SubSettings(如Data usage)?
hierarchyviewer中顯示SubSettings是因?yàn)槲覀冊(cè)趏nBuildStartFragmentIntent方法中做了特殊處理(詳見(jiàn)問(wèn)題二)。從shortcut進(jìn)入Settings時(shí)不顯示SubSettings是因?yàn)闆](méi)有走這個(gè)函數(shù),因此就不會(huì)顯示為SubSettings了(詳見(jiàn)問(wèn)題六)。
問(wèn)題八、Settings.java中很多繼承自它的內(nèi)部類(lèi)都是空實(shí)現(xiàn),為什么要寫(xiě)這些類(lèi)?
我們從這些內(nèi)部類(lèi)的名字可以看出它們的主要作用,如BluetoothSettingsActivity是針對(duì)藍(lán)牙設(shè)置的??諏?shí)現(xiàn)說(shuō)明他們都將使用Settings.java中的函數(shù)(注意private的屬*和方法的訪(fǎng)問(wèn)權(quán)限問(wèn)題)。聲明這些Activity的原因我認(rèn)為主要是為了提高各個(gè)設(shè)置項(xiàng)、整個(gè)Settings的靈活*,方便開(kāi)發(fā)者進(jìn)行擴(kuò)展。除此之外的一些原因:可以讓我們?yōu)閱为?dú)的設(shè)置項(xiàng)添加 shortcut(如data usage),因?yàn)閯?chuàng)建shortcut使用queryIntentActivities查詢(xún)使用的activity;允許其它程序訪(fǎng)問(wèn)單獨(dú)的設(shè)置項(xiàng);結(jié)構(gòu)設(shè)計(jì)需要,啟動(dòng)Activity會(huì)讀取meta-data信息;某些設(shè)置項(xiàng)不想使用SubSettings的屬*。如,在Settings中點(diǎn)擊Bluetooth時(shí)使用BluetoothSettingsActivity,啟動(dòng)Bluetooth時(shí)將使用BluetoothSettingsActivity的屬*,如 android:clearTaskOnLaunch="true"。
寫(xiě)的不全,望補(bǔ)充~
聯(lián)系客服