Android M 设置流程简要分析

这份笔记主要是记录设置的大致流程,以便在后续项目中需要修改设置的时候以便快熟理解大的思路.

 

首先从入口开始,也就是从第一个Activity开始,在AndroidManifest.xml里面找到android.intent.category.LAUNCHER,这个activity-alias 就是要找的入口,其实这只是一个别名

真正想要看的是它的android:targetActivity="Settings",也就是

 
  1. android:name="Settings"
  2. android:taskAffinity="com.android.settings"
  3. android:label="@string/settings_label_launcher"
  4. android:launchMode="singleTask">
  5. android:priority="1">
  6. android:name="android.settings.SETTINGS" />
  7. android:name="android.intent.category.DEFAULT" />
  8. android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
  9. android:value="true" />
  10.  
  11. android:name="Settings"
  12. android:taskAffinity="com.android.settings"
  13. android:label="@string/settings_label_launcher"
  14. android:launchMode="singleTask"
  15. android:targetActivity="Settings">
  16. android:name="android.intent.action.MAIN" />
  17. android:name="android.intent.category.DEFAULT" />
  18. android:name="android.intent.category.LAUNCHER" />

 

打开这个Settings.java发现里面主要是定义了很多内部静态Activity,主要逻辑还是在其父类SettingsActivity.java 里面,那就从onCreate()开始

 
  1. @Override
  2. protected void onCreate(Bundle savedState) {
  3. super.onCreate(savedState);
  4. mExt = UtilsExt.getMiscPlugin(this);
  5.  
  6. // Should happen before any call to getIntent()
  7. getMetaData();//获取配置meta-data中的com.android.settings.FRAGMENT_CLASS属性的值,将值保存在mFragmentClass里面
  8.  
  9. final Intent intent = getIntent();
  10. if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
  11. getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
  12. }
  13.  
  14. mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
  15. Context.MODE_PRIVATE);
  16.  
  17. // Getting Intent properties can only be done after the super.onCreate(...)
  18. final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
  19.  
  20. mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
  21. intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
  22.  
  23. final ComponentName cn = intent.getComponent();
  24. final String className = cn.getClassName();
  25.  
  26. mIsShowingDashboard = className.equals(Settings.class.getName());
  27.  
  28. // This is a "Sub Settings" when:
  29. // - this is a real SubSettings
  30. // - or :settings:show_fragment_as_subsetting is passed to the Intent
  31. final boolean isSubSettings = this instanceof SubSettings ||
  32. intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
  33.  
  34. // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
  35. if (isSubSettings) {
  36. // Check also that we are not a Theme Dialog as we don't want to override them
  37. final int themeResId = getThemeResId();
  38. if (themeResId != R.style.Theme_DialogWhenLarge &&
  39. themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
  40. setTheme(R.style.Theme_SubSettings);
  41. }
  42. }
  43.  
  44. setContentView(mIsShowingDashboard ?
  45. R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
  46.  
  47. mContent = (ViewGroup) findViewById(R.id.main_content);
  48.  
  49. getFragmentManager().addOnBackStackChangedListener(this);
  50.  
  51. if (mIsShowingDashboard) {
  52. // Run the Index update only if we have some space
  53. if (!Utils.isLowStorage(this)) {
  54. Index.getInstance(getApplicationContext()).update();
  55. } else {
  56. Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
  57. }
  58. }
  59.  
  60. if (savedState != null) {
  61. // We are restarting from a previous saved state; used that to initialize, instead
  62. // of starting fresh.
  63. mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
  64. mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
  65.  
  66. setTitleFromIntent(intent);
  67.  
  68. ArrayList<DashboardCategory> categories =
  69. savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
  70. if (categories != null) {
  71. mCategories.clear();
  72. mCategories.addAll(categories);
  73. setTitleFromBackStack();
  74. }
  75.  
  76. mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
  77. mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
  78. mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
  79. 1 /* one home activity by default */);
  80. } else {
  81. if (!mIsShowingDashboard) {
  82. mDisplaySearch = false;
  83. // UP will be shown only if it is a sub settings
  84. if (mIsShortcut) {
  85. mDisplayHomeAsUpEnabled = isSubSettings;
  86. } else if (isSubSettings) {
  87. mDisplayHomeAsUpEnabled = true;
  88. } else {
  89. mDisplayHomeAsUpEnabled = false;
  90. }
  91. setTitleFromIntent(intent);
  92.  
  93. Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
  94. switchToFragment(initialFragmentName, initialArguments, true, false,
  95. mInitialTitleResId, mInitialTitle, false);
  96. } else {
  97. // No UP affordance if we are displaying the main Dashboard
  98. mDisplayHomeAsUpEnabled = false;
  99. // Show Search affordance
  100. //mDisplaySearch = true;
  101. mDisplaySearch = false;//zhangle add :remove Search
  102. mInitialTitleResId = R.string.dashboard_title;
  103. switchToFragment(DashboardSummary.class.getName(), null, false, false,
  104. mInitialTitleResId, mInitialTitle, false);
  105. }
  106. }
  107. mActionBar = getActionBar();
  108. if (mActionBar != null) {
  109. mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
  110. mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
  111. }
  112. mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
  113.  
  114. // see if we should show Back/Next buttons
  115. if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
  116.  
  117. View buttonBar = findViewById(R.id.button_bar);
  118. if (buttonBar != null) {
  119. buttonBar.setVisibility(View.VISIBLE);
  120.  
  121. Button backButton = (Button)findViewById(R.id.back_button);
  122. backButton.setOnClickListener(new OnClickListener() {
  123. public void onClick(View v) {
  124. setResult(RESULT_CANCELED, getResultIntentData());
  125. finish();
  126. }
  127. });
  128. Button skipButton = (Button)findViewById(R.id.skip_button);
  129. skipButton.setOnClickListener(new OnClickListener() {
  130. public void onClick(View v) {
  131. setResult(RESULT_OK, getResultIntentData());
  132. finish();
  133. }
  134. });
  135. mNextButton = (Button)findViewById(R.id.next_button);
  136. mNextButton.setOnClickListener(new OnClickListener() {
  137. public void onClick(View v) {
  138. setResult(RESULT_OK, getResultIntentData());
  139. finish();
  140. }
  141. });
  142.  
  143. // set our various button parameters
  144. if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
  145. String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
  146. if (TextUtils.isEmpty(buttonText)) {
  147. mNextButton.setVisibility(View.GONE);
  148. }
  149. else {
  150. mNextButton.setText(buttonText);
  151. }
  152. }
  153. if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
  154. String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
  155. if (TextUtils.isEmpty(buttonText)) {
  156. backButton.setVisibility(View.GONE);
  157. }
  158. else {
  159. backButton.setText(buttonText);
  160. }
  161. }
  162. if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
  163. skipButton.setVisibility(View.VISIBLE);
  164. }
  165. }
  166. }
  167.  
  168. mHomeActivitiesCount = getHomeActivitiesCount();
  169. }

其中第7行是获取配置meta-data中的com.android.settings.FRAGMENT_CLASS属性的值,将值保存在mFragmentClass里面,后面会用到这个变量,其实这个变量保存就是我们要打开的Fragment的名称.

第14行的mDevelopmentPreferences 后面主要用来判断是否在设置的一级菜单列表中显示开发者选项.

第20 行的mIsShortcut在设置快捷方式的会用到,也就是在桌面创建快捷图标的时候会用到.

第26行的mIsShowingDashboard 用来判断是否显示的一级菜单,因为还有更深的二级菜单.同时这个变量将决定在44行的的setContentView时用的是什么布局文件,一级菜单页面和二级菜单页面是不一样,因为二级菜单页面是有返回按钮和上一步和下一步的按钮,这个在设置-安全里面设置密码的时候就会有体现,其中的底部的2个button也就是在settings_main_prefs 里面配置的.

同时81行到106行的这个if-else 也是有这儿mIsShowingDashboard  决定的,如果是DashBoard页面(一级菜单)就由来DashboardSummary处理.如果不是就由获取的指定的initialFragmentName 来处理.这里都是有方法switchToFragment()来处理的,只是传入的参数不同罢了.

上面提到的initialFragmentName 是在重写父类的getIntent()方法里面设置的.

 

DashBoard 这个列表并不是普通的ListView是不一样的,上面其实已经提过了,这儿DashBoard列表是通过DashboardSummary这个Fragment来实现的.整个设置的滑动列表是显示在ScrollView里面的.

DashboardSummary.java

 
  1. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  2. Bundle savedInstanceState) {
  3.  
  4. mLayoutInflater = inflater;
  5. mExt = UtilsExt.getMiscPlugin(this.getActivity());
  6.  
  7. final View rootView = inflater.inflate(R.layout.dashboard, container, false);
  8. mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
  9.  
  10. return rootView;
  11. }

layout: dashboard

 
  1. xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:id="@+id/dashboard"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:scrollbarStyle="outsideOverlay"
  6. android:clipToPadding="false">
  7.  
  8. android:id="@+id/dashboard_container"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent"
  11. android:layout_gravity="center_horizontal"
  12. android:paddingStart="@dimen/dashboard_padding_start"
  13. android:paddingEnd="@dimen/dashboard_padding_end"
  14. android:paddingTop="@dimen/dashboard_padding_top"
  15. android:paddingBottom="@dimen/dashboard_padding_bottom"
  16. android:clipToPadding="false"
  17. android:orientation="vertical"
  18. />
  19.  

我们在设置里面看到的可滑动的一级菜单列表就是在这个dashboard_container里面的.

在DashboardSummary.java的onResume()里面会使用Handler来执行rebuildUI()开始解析layout.dashboard_category 文件,并生成对应的View视图.

 
  1. private void rebuildUI(Context context) {
  2. if (!isAdded()) {
  3. Log.w(LOG_TAG, "Cannot build the DashboardSummary UI yet as the Fragment is not added");
  4. return;
  5. }
  6.  
  7. long start = System.currentTimeMillis();
  8. final Resources res = getResources();
  9.  
  10. mDashboard.removeAllViews();
  11.  
  12. List<DashboardCategory> categories =
  13. ((SettingsActivity) context).getDashboardCategories(true);
  14.  
  15. final int count = categories.size();
  16.  
  17. for (int n = 0; n < count; n++) {
  18. DashboardCategory category = categories.get(n);
  19.  
  20. View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mDashboard,
  21. false);
  22.  
  23. TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title);
  24. categoryLabel.setText(category.getTitle(res));
  25.  
  26. ViewGroup categoryContent =
  27. (ViewGroup) categoryView.findViewById(R.id.category_content);
  28.  
  29. final int tilesCount = category.getTilesCount();
  30. for (int i = 0; i < tilesCount; i++) {
  31. DashboardTile tile = category.getTile(i);
  32.  
  33. DashboardTileView tileView = new DashboardTileView(context);
  34. updateTileView(context, res, tile, tileView.getImageView(),
  35. tileView.getTitleTextView(), tileView.getStatusTextView());
  36.  
  37. tileView.setTile(tile);
  38. if(tile != null && tile.extras != null && tile.extras.containsKey(CUSTOMIZE_ITEM_INDEX)){
  39. int index = tile.extras.getInt(CUSTOMIZE_ITEM_INDEX, -1);
  40. categoryContent.addView(tileView, index);
  41. } else {
  42. categoryContent.addView(tileView);
  43. }
  44. }
  45.  
  46. // Add the category
  47. mDashboard.addView(categoryView);
  48. }
  49. long delta = System.currentTimeMillis() - start;
  50. Log.d(LOG_TAG, "rebuildUI took: " + delta + " ms");
  51. }

从上面的代码中可以发现每一个设置项对应的视图就是DashboardTileView,相关的标题,icon,要打开的对应的页面都是保存在DashboardTile里面的.

接着看看DashboardTileView,

 
  1. public DashboardTileView(Context context, AttributeSet attrs) {
  2. super(context, attrs);
  3.  
  4. //zhangle update layout res at 20150608 dashboard_tile -> dashboard_tile_doov
  5. final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile_doov, this);
  6.  
  7. mImageView = (ImageView) view.findViewById(R.id.icon);
  8. mTitleTextView = (TextView) view.findViewById(R.id.title);
  9. mStatusTextView = (TextView) view.findViewById(R.id.status);
  10. mDivider = view.findViewById(R.id.tile_divider);
  11.  
  12. setOnClickListener(this);
  13. setBackgroundResource(R.drawable.dashboard_tile_background);
  14. setFocusable(true);
  15. }

在上面的代码中可以看到我们点击每一个设置项的事件实际就是在DashboardTileView 的构造函数中设置的处理事件.

 

 

 

 

你可能感兴趣的:(Android,源码分析)