黑馬程序員 Android Settings中的幾個問題

當我們點擊主界面的header後會顯示與該header相關的設置界面。大部分(如Display的詳細設置界面)都是通過繼承PreferenceFragment來實現的;有一部分是在settings_headers.xml中聲明<intent>,當被點擊時(觸發PreferenceActivity的onHeaderClick())將會通過startActivity來啓動在<intent>節點中聲明的targetClass(如設置中的Add account)。

問題一、Settings的主界面是怎麼實現的?

問題二、爲什麼使用hierarchyviewer 時Settings中的很多界面顯示的都是SubSettings?

問題三、hierarchyviewer 中顯示SubSetting時如何確定我進入的是哪個fragment?

問題四、點擊設置界面的某一個header時,設置界面是如何切換的?

問題五、Settings.java中getMetaData與getStartingFragmentClass這兩個函數是否有點矛盾?

問題六、Settings的shortcut是如何創建的?從shortcut進入Settings的流程是什麼?

問題七、爲什麼我從Settings的shortcut進入時,hierarchyviewer顯示的就不是SubSettings(如Data usage)?

問題八、Settings.java中很多繼承自它的內部類都是空實現,爲什麼要寫這些類?

-----------------------------------------------------------------------------------------------------------------------------------------

由於項目需要,本人就對Android中的Settings進行了解析,希望能幫到對Settings有興趣的同志們~

-----------------------------------------------------------------------------------------------------------------------------------------

問題一、Settings的主界面是怎麼實現的?

爲了能適應平板和手機,Settings採用了PreferenceActivity和PreferenceFragment結合的實現方式。

Settings.java繼承自PreferenceActivity,是Settings的主界面,它通過loadHeadersFromResource函數(api level 11)加載res/xml/settings_headers.xml來構造界面。在settings_headers.xml中聲明瞭要在Settings主界面顯示的各個header(如Sound、Display等)。Settings.HeaderAdapter將其中的header分爲三類。在Settings.HeaderAdapter中的getView方法中根據header的類型使用不同的佈局文件。

爲header劃分類型的函數


       static int getHeaderType(Header header) {

           if (header.fragment == null && header.intent == null) {

               return HEADER_TYPE_CATEGORY; // 因爲沒有指明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; // 針對特定的三個header,分別爲Wi-Fi、Bluetooth和Mobile data

           } else {

               return HEADER_TYPE_NORMAL;

           }

       }


問題二、爲什麼使用hierarchyviewer 時Settings中的很多界面顯示的都是SubSettings?

要解決這個問題我們先要清楚爲什麼會寫一個SubSettings.java繼承自Settings.java?

SubSettings.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在聲明時指定了android:launchMode="singleTask"。

我們知道,要顯示Fragment的內容,我們就需要爲其指定一個Activity。而Settings中的很多設置界面是由PreferenceFragment來完成的,當然也需要我們指定Activity.

onBuildStartFragmentIntent函數會爲我們構造一個顯示Fragment的Intent對象(該函數的註釋寫的非常明白).Settings.java重寫了這個函數(注,重寫時它調用了super的該方法),在爲intent對象setClass時都使用SubSettings.java.(注:在settings_headers.xml指定了intent的header是不會觸發onBuildStartFragmentIntent的)

結果就是,Settings中大部分fragment都是使用的SubSettings這個Activity來顯示。由於hierarchyviewer只是顯示當前界面使用的Activity(不能顯示這個界面是由哪個Fragment構造的),所以我們使用hierarchyviewer 對Settings進行觀察時很多設置界面顯示的是SubSettings。

問題三、hierarchyviewer 中顯示SubSetting時如何確定我進入的是哪個fragment?

在res/xml/settings_headers.xml中聲明瞭各個header被點擊後使用的fragment。我們可以根據這個文件確定我們進入的fragment。

例如,當我們點擊Display時hierarchyviewer 中顯示SubSetting。我們通過查找settings_headers就可知道使用的是哪個fragment。

Display這個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這個fragment

問題四、點擊設置界面的某一個header時,設置界面是如何切換的?

點擊設置界面的header時,會觸發Settings中onHeaderClick函數,主要的處理都在其父類PreferenceActivity的onHeaderClick中實現的。如果這個header指定了fragment,在mSinglePane(標識是否爲小屏幕設備。如,區分手機還是平板)爲true時,會調用startWithFragment方法,在startWithFragment方法中將調用onBuildStartFragmentIntent方法來構造intent對象(重要),最後使用該intent對象啓動一個activity來顯示fragment。

以點擊Settings中的Display爲例.

(Bluetooth同理,只不過啓動的Activity變爲BluetoothSettingsActivity(繼承自Settings,但是沒有實現重寫任何方法,所以與SubSettings是一樣的處理),fragment變爲 com.android.settings.bluetooth.BluetoothSettings)

fragment是com.android.settings.DisplaySettings,activity是com.android.settings.SubSettings.(fragment是由onHeaderClick函數傳入的,activity是由onBuildStartFragmentIntent()指定的)

執行startActivity後將啓動SubSettings.java。即我們將會再一次執行SubSettings和PreferenceActivity的onCreate方法(因爲Settings.java的onCreate方法調用了super.onCreate()),但是這次並不會進入Settings的主界面,因爲我們的使用的intent對象是有很大不同的。這一次onCreate函數(PreferenceActivity)中的initialFragment 將被初始化爲com.android.settings.DisplaySettings,然後我們將進入switchToHeader(),最後switchToHeaderInner會取得FragmentTransaction對象,然後執行了transaction.replace(com.android.internal.R.id.prefs, f).就這樣把我們的fragment顯示出來了。在onCreate中會對其他view的visibility進行設置,以保證只顯示prefs。如,將com.android.internal.R.id.headers的visibility設置爲VIEW.GONE.

注:PreferenceActivity的佈局文件爲preference_list_content.xml

問題五、Settings.java中getMetaData與getStartingFragmentClass這兩個函數是否有點矛盾?

這兩個函數可以說是相輔相成的(好官方,^_^)。getMetaData會從AndroidManifest.xml中讀取Activity的<meta-data>節點的數據;getStartingFragmentClass則從啓動Activity的intent中讀取數據。這兩個函數會對讀取到的數據進行整合,getStartingFragmentClass依賴於getMetaData讀取到的數據,但是它也可能對數據作出修改(爲了兼容*,如對原有manage apps類進行特殊處理)。

問題六、Settings的shortcut是如何創建的?從shortcut進入Settings的流程是什麼?

創建Settings的shortcut時Luancher將會啓動CreateShortcut,創建shortcut所需的intent對象將會由CreateShortcut和其父類LuancherActivity共同構建(詳見 CreateShortcut的onListItemClick),這時創建的Intent對象使用的就不是SubSettings了(LuancherActivity中intentForPosition函數執行setClassName()時使用的參數並不是SubSettings).

CreateShortcut中列出了可以創建shortcut的設置項,這些設置項怎樣檢索出來的?

原來,在創建LuancherActivity的ActivityAdapter對象時,其構造函數中執行了makeListItems函數,該函數將使用PackageManager的queryIntentActivities來根據intent對象查詢符合條件的activity。使用的intent是從getTargetIntent函數返回的。不難發現,要想在CreateShortcut中顯示,Activity在必須要有

<category android:name="com.android.settings.SHORTCUT" />

如果我們想將Security設置項添加到shortcut列表,我們只需要在androidmanifest.xml中聲明Settings$SecuritySettingsActivity部分加上

<category android:name="com.android.settings.SHORTCUT" />

即可。

回到正題,點擊shortcut進入Settings時,傳入的Intent對象中包含了目標fragment和目標activity以及其他信息。PreferenceActivity得到了足夠多的信息,因此在onCreate中將依次執行switchToHeader()->setSelectedHeader(null)->switchToHeaderInner()->transaction.replace(com.android.internal.R.id.prefs, f);

這樣就完成了fragment的顯示(使用的activity是從intent解析出來的.在switchToHeaderInner中執行Fragment.instantiate時使用的Context是this!!)。不像執行onHeaderClick那樣會執行函數onBuildStartFragmentIntent(Settings中重寫了該函數)來重新指定我們使用的Activity。

問題七、爲什麼我從Settings的shortcut進入時,hierarchyviewer顯示的就不是SubSettings(如Data usage)?

hierarchyviewer中顯示SubSettings是因爲我們在onBuildStartFragmentIntent方法中做了特殊處理(詳見問題二)。從shortcut進入Settings時不顯示SubSettings是因爲沒有走這個函數,因此就不會顯示爲SubSettings了(詳見問題六)。

問題八、Settings.java中很多繼承自它的內部類都是空實現,爲什麼要寫這些類?

我們從這些內部類的名字可以看出它們的主要作用,如BluetoothSettingsActivity是針對藍牙設置的。空實現說明他們都將使用Settings.java中的函數(注意private的屬*和方法的訪問權限問題)。聲明這些Activity的原因我認爲主要是爲了提高各個設置項、整個Settings的靈活*,方便開發者進行擴展。除此之外的一些原因:可以讓我們爲單獨的設置項添加 shortcut(如data usage),因爲創建shortcut使用queryIntentActivities查詢使用的activity;允許其它程序訪問單獨的設置項;結構設計需要,啓動Activity會讀取meta-data信息;某些設置項不想使用SubSettings的屬*。如,在Settings中點擊Bluetooth時使用BluetoothSettingsActivity,啓動Bluetooth時將使用BluetoothSettingsActivity的屬*,如 android:clearTaskOnLaunch="true"。

寫的不全,望補充~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章