1. 程式人生 > >Android的Setting的顯示載入分析

Android的Setting的顯示載入分析

1、Setting.java的分析
在系統中目錄:
/home/quan/code/MT6750-PRB/packages/apps/Settings/src/com/android/settings/Settings.java

public class Settings extends SettingsActivity {
    public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
    public static class WirelessSettingsActivity extends
SettingsActivity {
/* empty */ } public static class SimSettingsActivity extends SettingsActivity { /* empty */ } public static class TetherSettingsActivity extends SettingsActivity { /* empty */ } public static class VpnSettingsActivity extends SettingsActivity { /* empty */ } public static class
DateTimeSettingsActivity extends SettingsActivity {
/* empty */ } .... }

從結構上看Settings繼承的是SettingsActivity這個類,內部宣告的是一些空實現體的靜態內部類。這樣設計的目的是方便啟動特定獨立的Settings選項而建立的,例如在某個應用裡需要設定無線那麼只需要啟動 WirelessSettingsActivity 就可以了。

2、SettingsActivity.java
在Setting.java中沒有啟動的邏輯,啟動的邏輯實現在SettingsActivity.java中。這裡只是分析Setting顯示介面的載入過程。
在系統中目錄:
/home/quan/code/MT6750-PRB/packages/apps/Settings/src/com/android/settings/SettingsActivity.java
1)Activity的第一步:onCreate()方法
關鍵點:
(1)獲得額外的資料
呼叫方法getMetaData()方法獲得資料,如果獲得資料成功,則表示不是直接啟動設定的(比如啟動的是:BluetoothSettingsActivity),則不會顯示所有的設定項,只顯示具體的設定項(比如藍芽設定)。
(2)判斷當前的類是哪個具體的類

boolean mIsShowingDashboard = className.equals(Settings.class.getName());

如果是通過點選桌面的設定進入,那麼mIsShowingDashboard的值就為true。

final boolean isSubSettings = className.equals(SubSettings.class.getName()) ||
 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);

那麼isSubSettings則為false。
(此值為true代表是要進入子模組,比如:

BluetoothSettingsActivity) 
 setContentView(mIsShowingDashboard ?
                R.layout.settings_main_dashboard : R.layout.settings_main_prefs);

通過前面的值可以判斷當前載入的佈局應該是R.layout.settings_main_dashboard這個佈局檔案。
(3)R.layout.settings_main_dashboard檔案

<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"
             />

通過佈局檔案的內容發現,載入的是幀佈局,其作用後續分析。
(4)載入fragment

// 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, false, false,
               mInitialTitleResId, mInitialTitle, false);

由於mIsShowingDashboard是true的原因,程式碼執行了上面的這段程式碼。可以發先執行了switchToFragment()方法,目的是切換到對應的fragment上(幀佈局起作用了吧^_^),也就是DashboardSummary這個fragment。接下來看一下DashboardSummary.java這個類了。

3、DashboardSummary.java
系統目錄:
/home/quan/code/MT6750-PRB/packages/apps/Settings/src/com/android/settings/dashboard/DashboardSummary.java
dashboard中文意思是儀表盤,這裡是指DashboardSummary就是用來顯示Settings所有的選項的。

public class DashboardSummary extends InstrumentedFragment{...}----->這是個fragment。

這裡從onCreateView方法開始分析:
1)onCreateView()方法

mLte4GEnabler = new Lte4GEnabler(getActivity(), new Switch(getActivity())); final View rootView = inflater.inflate(R.layout.dashboard, container, false); mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);

從程式碼中可以看出來,DashboardSummary這個Fragment載入的dashboard.xml這個佈局檔案。下圖是setting介面的樣式(這裡是Android4.3的截圖,5.0有所區別,但是不影響對程式碼的分析。)

接下來看下dashboard.xml佈局:
DashboardSummary這個Fragment的佈局檔案是dashboard.xml,其結構如下:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dashboard"
...此處內容省略...
>
        <LinearLayout
android:id="@+id/dashboard_container"
    ....此處內容省略...
    />
</ScrollView>

最外層是ScrollView,內層是LinearLayout,在上圖中的setting介面中,最外層是可滑動的原因就在此,在上圖中看見的所有選項都是放在LinearLayout中的,只不過裡邊的內容是動態新增上去的。

走完了DashboardSummary中的onCreateView()方法,接下來繼續分析onResume方法。
2)onResume()方法

...
sendRebuildUI();//顯示各個設定的選項
...

sendRebuildUI();在此方法中呼叫了更新顯示介面的訊息(MSG_REBUILD_UI),在handler收到訊息後呼叫了rebuildUI();方法。
3)rebuildUI()方法
在這裡首先列出以下問題:
問題1:dashboard_container這個LinearLayout都載入了些什麼內容?
問題2:資料資源是如何適配的?
【1】首先來看LinearLayout中都載入了什麼:
通過閱讀rebuildUI方法的原始碼可以知道,setting的LinearLayout(問題1)中只加載了categoryView,那麼categoryView又是什麼?它是一個View物件,其佈局檔案是dashboard_category.xml。內容如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/category"
        ...此處內容省略...
>
    <TextView android:id="@+id/category_title"
            ...此處內容省略...
            />

    <com.android.settings.dashboard.DashboardContainerView
            android:id="@+id/category_content"
            ...此處內容省略...
            />
</LinearLayout>

這個佈局與實際的顯示部分的對應關係如下:
這裡寫圖片描述
從分析可以看出LinearLayout中只加載了categoryView,其他的顯示的內容都是在DashboardContainerView中載入完成的。DashboardContainerView中載入了DashboardTileView的物件。通過對原始碼的分析可以知道,DashboardTileView其實就是FrameLayout,也有對應的佈局檔案dashboard_tile_switch.xml或dashboard_tile.xml,這裡就不在深入分析了。接下來分析第二個問題:資料適配的問題。
【2】資料資源的適配
在這裡說明一點,Setting中顯示內容的條目數量是由資源數量決定的,即要有多少資源才能顯示多少個檢視。而這個資源就定義在dashboard_categories.xml中。
在rebuildUI()方法中,在顯示UI之前會去載入資源:

List<DashboardCategory> categories =
                ((SettingsActivity) context).getDashboardCategories(true);

getDashboardCategories()方法:

public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
        if (forceRefresh || mCategories.size() == 0) {
            buildDashboardCategories(mCategories);
        }
return mCategories;
    }

主要看buildDashboardCategories()方法中的loadCategoriesFromResource方法:
這個方法比較多這裡不呈現,這個方法主要是在做xml的解析工作,將解析好的資料資源存放到了List categories這個集合中。其中dashboard_categories.xml的dashboard-category節點對應的是DashboardCategory的物件,dashboard-tile節點對應的是DashboardTile的物件,而DashboardCategory物件內部有List tiles的集合物件,所解析出來的DashboardTile都有DashboardCategory保管,而每個DashboardCategory物件又由categories來保管。
就這樣資料資源就得到了,然後在sendRebuildUI()方法中就是一個迴圈遍歷新增資料顯示的過程,dashboard_categories.xml的dashboard-category節點最終對應了DashboardTileView的物件。接下來就在看看這個DashboardTileView和dashboard-category到底是如何跳轉到具體的某個Fragment的。

附:DashboardTileView和dashboard-category分析

<!-- Wifi -->
        <dashboard-tile
                android:id="@+id/wifi_settings"
                android:title="@string/wifi_settings_title"
                android:fragment="com.android.settings.wifi.WifiSettings"
                android:icon="@drawable/ic_settings_wireless"
                />
 <!-- Bluetooth -->
        <dashboard-tile
                android:id="@+id/bluetooth_settings"
                android:title="@string/bluetooth_settings_title"
                android:fragment="com.android.settings.bluetooth.BluetoothSettings"
                android:icon="@drawable/ic_settings_bluetooth2"
                />
dashboard_categories.xml中的片段擷取
@Override
    public void onClick(View v) {
        if (mTile.fragment != null) {
            Utils.startWithFragment(getContext(), mTile.fragment, mTile.fragmentArguments, null, 0,mTile.titleRes, mTile.getTitle(getResources()));
        } else if (mTile.intent != null) {
            getContext().startActivity(mTile.intent);
        } else if (mTile.getTitle(getResources()).equals(
                getResources().getString(R.string.lte_4g_settings_title))) {
            if (null != mSwitch) {
                mSwitch.setChecked(!mSwitch.isChecked());
            }
        }
    }
DashboardTileView的點選方法

所以當點選DashboardTileView的某個物件時就會跳轉至一個具體的Fragment,而這個Fragment已經在dashboard-category節點中宣告。

到此Setting介面的顯示載入以及點選跳轉的大部分邏輯都分析完了。