編輯:關於Android編程
Setting是android系統很重要的模塊,這個模塊並不是很復雜,這部分也一直在看,很多時候都是在看某個具體的選項,比如WLAN,藍牙這樣具體的源碼,但是對於主界面的布局以及結構並不清楚。
在使用Hierarchy Viewer工具可以看到Settings模塊的主界面顯示的是Settings,
com.android.settings/com.android.settings.Settings
在進入設置的子界面的時候,顯示永遠是SubSettings,
com.android.settings/com.android.settings.SubSettings
這樣我就感覺有點奇怪,為什麼所有的子界面顯示的都是SubSettings。
這幾天搜了相關資料和結合源碼看了一下這部分的邏輯,簡單分析如下。
在SettingsActivity的onCreate方法中,通過判斷當前是Settings還是SubSettings來確定用什麼布局來顯示
@Override protected void onCreate(Bundle savedState) { super.onCreate(savedState); ..... mIsShowingDashboard = className.equals(Settings.class.getName()); setContentView(mIsShowingDashboard ? R.layout.settings_main_dashboard : R.layout.settings_main_prefs); .... .... }
這裡會有兩個問題,Settings作為主界面,加載的是settings_main_dashboard.xml文件,下面是這個xml文件具體內容
<framelayout android:background="@color/dashboard_background_color" android:id="@+id/main_content" android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"> </framelayout>
如果都是這個布局的話,那麼主界面的不同item是如何顯示出來的呢?
主界面settings_main_dashboard中是使用DashboardSummary(Fragment)進行填充。由於設置的主界面Settings和設置的子界面SubSettings都是繼承於SettingsActivity的,Setting是和SubSettings裡面是空的,所以會執行父類SettingsActivity的onCreate方法。
在SettingsActivity的onCreate方法後面,在這裡會switchToFragment進行替換,
將其替換為DashboardSummary。DashboardSummary同樣是一個Fragment,通過mIsShowingDashboard判斷是否將DashboardSummary替換過來,而這裡mIsShowingDashboard的值是通過判斷當前是Settings還是SubSettings來獲得的。
下面是switchToFragment方法的部分源碼:
if (savedState != null) { ..... ..... } else { if (!mIsShowingDashboard) { ...... ...... switchToFragment(initialFragmentName, initialArguments, true, false,mInitialTitleResId, mInitialTitle, false); } else { switchToFragment(DashboardSummary.class.getName(), null, false, false,mInitialTitleResId, mInitialTitle, false); } }
然後去看DashboardSummary這個類,它是一個Fragment。DashboardSummary繼承了PreferenceFragment,並且在它的onCreateView方法中,加載的XML布局是dashboard.xml,dashboard.xml的布局如下,使用ScrollView嵌套了一個豎直的線性布局,這樣,設置的主界面就是可以滾動的垂直的線性結構。
這樣,設置主頁面Settings的整體結構就出來了,接下來看這界面的選項列表是如何加載出來的。
注意到在DashboardSummary的onResume方法中,有個方法為sendRebuildUI,這個方法通過Handler發送Message來通知界面更新UI,更新UI的方法為rebuildUI。
在rebuildUI這個方法中,通過調用SettingsActivity中的getDashboardCategories來獲得主界面的選項列表,在SettingsActivity中的getDashboardCategories方法中,通過調用buildDashboardCategories來從布局文件中將Setting主界面的選項解析出來,這個布局為dashboard_categories.xml。下面截取的是dashboard_categories.xml部分布局文件,大神board-categories節點表示的大的選項的分類,dashboard-tile則表示的是具體的選項比如wifi,藍牙等。
上面紅框圈住的為DashboardTitle,下面紅框圈住的為DashboardTitleView,DashboardTitle對應的是xml布局中的dashboard-category這個節點,而DashboardTitleView則對應的是dashboard-title這個節點。
vcnlfd2lyZWxlc3NfbmV0d29ya3M="> ..... .....
通過以上一系列的操作,Setting模塊的主界面的布局就加載出來了。
在Settings類中,定義了大量靜態的內部類,但是都是空的,並未實現。
定義的這些靜態內部類主要用於跳轉用的,比如從SystemUI跳轉至Setting的某一個頁面,另外這些靜態內部類在AndroidManifest.xml文件中通過meta-data將相應的Fragment綁定起來。
這裡是如何通過meta-data將相應的Fragment進行綁定的,可以看看這個博客:Android5.0 Settings各個子模塊跳轉和布局實現
第二個問題,進入設置子界面,也就是二級界面的時候,Hierarchy Viewer工具上顯示的SubSettings,接下來分析這一塊。
由SubSettingsActivity的onCreate方法可知,SubSettings加載的布局文件是settings_main_prefs.xml,布局文件的內容如下。
<framelayout android:background="?attr/preferenceBackgroundColor" android:id="@+id/main_content" android:layout_height="match_parent" android:layout_width="match_parent"> </framelayout>
同樣的,SubSettings的Fragment的替換也是在SettingsActivity的onCreate方法中處理,注意到在onCreate方法的後面,如下,當mIsShowingDashboard為false時,會調用
switchToFragment(),這時第一個參數穿進去的是initalFragmentName,initalFragmentName這個參數就是對應的就是子界面的Fragment,在onCreate方法內先後執行了getMetaData和getIntent兩個方法後,會通過PackageManager獲取Activity的信息,得到對應的Activity的meta-data中key為com.android.settings.FRAGMENT_CLASS的值value,比如
wifi設置模塊的值就是com.android.settings.WirelessSettings,然後再通過構造一個intent,並且給它增加了一個特殊的鍵值對,key為:settings:show_fragment,value為mFragmentClass指定的Fragment類名。
if (savedState != null) { ..... ..... } else { if (!mIsShowingDashboard) { ...... ...... switchToFragment(initialFragmentName, initialArguments, true, false,mInitialTitleResId, mInitialTitle, false); } else { switchToFragment(DashboardSummary.class.getName(), null, false, false,mInitialTitleResId, mInitialTitle, false); } }
這樣,SubSettings中settings_main_prefs.xml對應的Fragment就會被響應的Fragment替換過來了。
這個部分介紹的是Settings子界面是如何渲染出來的。
最後介紹下,由主界面的Item項是如何進行跳轉的,即由主界面的選項是如何響應用戶的點擊事件跳轉至子界面的?
由於主界面的布局都是采用DashboardCategory和DashboardTileView進行構造的,DashboardCategory相當於父控件,而DashboardTileView則為子控件,一般DashboardCategory一般可以包含多個DashboardTileView。
查看DashboardTileView這個類的源碼,會發現這裡面有個onClick方法,這樣用戶的點擊事件就可以響應了。當用戶點擊的時候,會調用Utils.startWithFragmen。
@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) { int numUserHandles = mTile.userHandle.size(); if (numUserHandles > 1) { ProfileSelectDialog.show(((Activity) getContext()).getFragmentManager(), mTile); } else if (numUserHandles == 1) { getContext().startActivityAsUser(mTile.intent, mTile.userHandle.get(0)); } else { getContext().startActivity(mTile.intent); } } }
在startFragment中,獲得點擊的item項對應的FragmentName,,然後構造一個Intent進行跳轉。
public static void startWithFragment(Context context, String fragmentName, Bundle args, Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut) { Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName, titleResId, title, isShortcut); if (resultTo == null) { context.startActivity(intent); } else { resultTo.startActivityForResult(intent, resultRequestCode); } } public static Intent onBuildStartFragmentIntent(Context context, String fragmentName, Bundle args, String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClass(context, SubSettings.class); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME, titleResPackageName); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut); return intent; }
在Android開發中,我們經常會用到ListView、GridView,每次編碼的時候都需要為他們寫對應的Adapter,寫多了就感覺很煩躁了,因為基本的編程思想都是一
相對與視圖動畫 ,屬性動畫(android3.0提出的) 使用條件:完全彌補了View anim System的缺陷,你可以為一個對象的任何屬性添加動畫,(View或者非
最近做一個項目類似於QQ空間,做到照片浏覽的功能,對於QQ空間中點擊圖片放大至全屏,感覺效果很贊,於是也做了個類似的效果。如下。 packag
Android-通過Java代碼來實現屬性動畫除了可以使用定義xml文件來設置動畫之外,還可以使用java代碼來進行控制動畫。示例如下:布局文件: 主活動: