Android L的settings界面实现和Android4.4版本有很大的不同,在Android L中,setting是使用dashboard等控件进行了重新实现。具体流程如下。
Android L Settings模块首界面为Settings,继承自SettingsActivity,而SettingsActivity继承自Activity。
首先看一下Settings.java代码,可以发现它没有重写任何SettingsActiviy的方法,也没有增加任何自己的方法,唯独增加了许多静态内部类,如:
/* * Settings subclasses for launching independently. */
public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ }
public static class SimSettingsActivity extends SettingsActivity { /* empty */ }
// ......
看注释可以知道,这些子类是为了启动特定独立的Settings选项而创建的,例如在某个应用里需要设置蓝牙那么只需要启动 BluetoothSettingsActivity 就可以了。
所以,Settings模块的启动流程直接看SettingsActivity就行了。
onCreate方法是Activity的生命周期第一步,带注释的onCreate()源码如下:
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
// 用来获取Activity的额外数据mFragmentClass,直接启动Settings模块不会获得这个数据
getMetaData();
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 = className.equals(SubSettings.class.getName()) ||
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);
}
}
// 设置contentview
setContentView(mIsShowingDashboard ?
R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
// 获取framelayout控件
mContent = (ViewGroup) findViewById(R.id.main_content);
getFragmentManager().addOnBackStackChangedListener(this);
if (mIsShowingDashboard) {
Index.getInstance(getApplicationContext()).update();
}
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) {
// Search is shown we are launched thru a Settings "shortcut". UP will be shown
// only if it is a sub settings
if (mIsShortcut) {
mDisplayHomeAsUpEnabled = isSubSettings;
mDisplaySearch = false;
} else if (isSubSettings) {
mDisplayHomeAsUpEnabled = true;
mDisplaySearch = true;
} else {
mDisplayHomeAsUpEnabled = false;
mDisplaySearch = 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;
mInitialTitleResId = R.string.dashboard_title;
// 跳转到DashboardSummary,这是构造UI真正的地方
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();
}
dashboard中文意思是仪表盘,这里是指DashboardSummary就是用来显示Settings所有选项的。而DashboardSummary是个Fragment,所以先执行了onCreateView方法。
在onCreateView中,就是inflate了一个View返回,这个view的布局如下:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dashboard" android:layout_width="match_parent" android:layout_height="match_parent" <!-- 设置滑动那个bar的样式 -->
android:scrollbarStyle="outsideOverlay"
android:clipToPadding="false">
<LinearLayout 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:orientation="vertical" />
</ScrollView>
DashboardSummary走完onCreateView方法后会走onResume,onResume会通过handler发送一个消息来构建UI界面的方法rebuildUI,然后一路下来又会调用到SettingsActivity的loadCategoriesFromResource(R.xml.dashboard_categories, categories);
这个dashboard_categories.xml其实是布局的内容的抽象,通过这个xml文件解析出来能够填充每一项的资源内容。
总结内容如下:
可以看出,如果我们想要改变Settings的UI,那我们主要需要改动如下几个自定义View和布局文件,分别是:
自定义view:
布局文件: