这份笔记主要是记录设置的大致流程,以便在后续项目中需要修改设置的时候以便快熟理解大的思路.
首先从入口开始,也就是从第一个Activity开始,在AndroidManifest.xml里面找到android.intent.category.LAUNCHER,这个activity-alias 就是要找的入口,其实这只是一个别名
真正想要看的是它的android:targetActivity="Settings",也就是
android:taskAffinity="com.android.settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask">
android:value="true" />
android:name="Settings"
android:taskAffinity="com.android.settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask"
android:targetActivity="Settings">
android:name="android.intent.action.MAIN" />
android:name="android.intent.category.DEFAULT" />
android:name="android.intent.category.LAUNCHER" />
打开这个Settings.java发现里面主要是定义了很多内部静态Activity,主要逻辑还是在其父类SettingsActivity.java 里面,那就从onCreate()开始
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
mExt = UtilsExt.getMiscPlugin(this);
// Should happen before any call to getIntent()
getMetaData();//获取配置meta-data中的com.android.settings.FRAGMENT_CLASS属性的值,将值保存在
mFragmentClass里面final Intent intent = getIntent();
if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
}
mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
Context.MODE_PRIVATE);
// Getting Intent properties can only be done after the super.onCreate(...)
final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
final ComponentName cn = intent.getComponent();
final String className = cn.getClassName();
mIsShowingDashboard = className.equals(Settings.class.getName());
// This is a "Sub Settings" when:
// - this is a real SubSettings
// - or :settings:show_fragment_as_subsetting is passed to the Intent
final boolean isSubSettings = this instanceof SubSettings ||
intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
// If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
if (isSubSettings) {
// Check also that we are not a Theme Dialog as we don't want to override them
final int themeResId = getThemeResId();
if (themeResId != R.style.Theme_DialogWhenLarge &&
themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
setTheme(R.style.Theme_SubSettings);
}
}
setContentView(mIsShowingDashboard ?
R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
mContent = (ViewGroup) findViewById(R.id.main_content);
getFragmentManager().addOnBackStackChangedListener(this);
if (mIsShowingDashboard) {
// Run the Index update only if we have some space
if (!Utils.isLowStorage(this)) {
Index.getInstance(getApplicationContext()).update();
} else {
Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
}
}
if (savedState != null) {
// We are restarting from a previous saved state; used that to initialize, instead
// of starting fresh.
mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
setTitleFromIntent(intent);
ArrayList<DashboardCategory> categories =
savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
if (categories != null) {
mCategories.clear();
mCategories.addAll(categories);
setTitleFromBackStack();
}
mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
1 /* one home activity by default */);
} else {
if (!mIsShowingDashboard) {
mDisplaySearch = false;
// UP will be shown only if it is a sub settings
if (mIsShortcut) {
mDisplayHomeAsUpEnabled = isSubSettings;
} else if (isSubSettings) {
mDisplayHomeAsUpEnabled = true;
} else {
mDisplayHomeAsUpEnabled = false;
}
setTitleFromIntent(intent);
Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
switchToFragment(initialFragmentName, initialArguments, true, false,
mInitialTitleResId, mInitialTitle, false);
} else {
// No UP affordance if we are displaying the main Dashboard
mDisplayHomeAsUpEnabled = false;
// Show Search affordance
//mDisplaySearch = true;
mDisplaySearch = false;//zhangle add :remove Search
mInitialTitleResId = R.string.dashboard_title;
switchToFragment(DashboardSummary.class.getName(), null, false, false,
mInitialTitleResId, mInitialTitle, false);
}
}
mActionBar = getActionBar();
if (mActionBar != null) {
mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
}
mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
// see if we should show Back/Next buttons
if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
View buttonBar = findViewById(R.id.button_bar);
if (buttonBar != null) {
buttonBar.setVisibility(View.VISIBLE);
Button backButton = (Button)findViewById(R.id.back_button);
backButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_CANCELED, getResultIntentData());
finish();
}
});
Button skipButton = (Button)findViewById(R.id.skip_button);
skipButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_OK, getResultIntentData());
finish();
}
});
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_OK, getResultIntentData());
finish();
}
});
// set our various button parameters
if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
if (TextUtils.isEmpty(buttonText)) {
mNextButton.setVisibility(View.GONE);
}
else {
mNextButton.setText(buttonText);
}
}
if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
if (TextUtils.isEmpty(buttonText)) {
backButton.setVisibility(View.GONE);
}
else {
backButton.setText(buttonText);
}
}
if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
skipButton.setVisibility(View.VISIBLE);
}
}
}
mHomeActivitiesCount = getHomeActivitiesCount();
}
其中第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
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mLayoutInflater = inflater;
mExt = UtilsExt.getMiscPlugin(this.getActivity());
final View rootView = inflater.inflate(R.layout.dashboard, container, false);
mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
return rootView;
}
layout: dashboard
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dashboard"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
android:clipToPadding="false">
android:id="@+id/dashboard_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:paddingStart="@dimen/dashboard_padding_start"
android:paddingEnd="@dimen/dashboard_padding_end"
android:paddingTop="@dimen/dashboard_padding_top"
android:paddingBottom="@dimen/dashboard_padding_bottom"
android:clipToPadding="false"
android:orientation="vertical"
/>
我们在设置里面看到的可滑动的一级菜单列表就是在这个dashboard_container里面的.
在DashboardSummary.java的onResume()里面会使用Handler来执行rebuildUI()开始解析layout.dashboard_category 文件,并生成对应的View视图.
private void rebuildUI(Context context) {
if (!isAdded()) {
Log.w(LOG_TAG, "Cannot build the DashboardSummary UI yet as the Fragment is not added");
return;
}
long start = System.currentTimeMillis();
final Resources res = getResources();
mDashboard.removeAllViews();
List<DashboardCategory> categories =
((SettingsActivity) context).getDashboardCategories(true);
final int count = categories.size();
for (int n = 0; n < count; n++) {
DashboardCategory category = categories.get(n);
View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mDashboard,
false);
TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title);
categoryLabel.setText(category.getTitle(res));
ViewGroup categoryContent =
(ViewGroup) categoryView.findViewById(R.id.category_content);
final int tilesCount = category.getTilesCount();
for (int i = 0; i < tilesCount; i++) {
DashboardTile tile = category.getTile(i);
DashboardTileView tileView = new DashboardTileView(context);
updateTileView(context, res, tile, tileView.getImageView(),
tileView.getTitleTextView(), tileView.getStatusTextView());
tileView.setTile(tile);
if(tile != null && tile.extras != null && tile.extras.containsKey(CUSTOMIZE_ITEM_INDEX)){
int index = tile.extras.getInt(CUSTOMIZE_ITEM_INDEX, -1);
categoryContent.addView(tileView, index);
} else {
categoryContent.addView(tileView);
}
}
// Add the category
mDashboard.addView(categoryView);
}
long delta = System.currentTimeMillis() - start;
Log.d(LOG_TAG, "rebuildUI took: " + delta + " ms");
}
从上面的代码中可以发现每一个设置项对应的视图就是DashboardTileView,相关的标题,icon,要打开的对应的页面都是保存在DashboardTile里面的.
接着看看DashboardTileView,
public DashboardTileView(Context context, AttributeSet attrs) {
super(context, attrs);
//zhangle update layout res at 20150608 dashboard_tile -> dashboard_tile_doov
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile_doov, this);
mImageView = (ImageView) view.findViewById(R.id.icon);
mTitleTextView = (TextView) view.findViewById(R.id.title);
mStatusTextView = (TextView) view.findViewById(R.id.status);
mDivider = view.findViewById(R.id.tile_divider);
setOnClickListener(this);
setBackgroundResource(R.drawable.dashboard_tile_background);
setFocusable(true);
}
在上面的代码中可以看到我们点击每一个设置项的事件实际就是在DashboardTileView 的构造函数中设置的处理事件.