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介面的顯示載入以及點選跳轉的大部分邏輯都分析完了。