Android L Settings实现

概述

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就行了。

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();
    }

DashboardSummary

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文件解析出来能够填充每一项的资源内容。

小结

总结内容如下:

  1. onCreate完成的任务是切换DashboardSummary这个Fragment,然后从dashboard_categories.xml中读取预先配置好的文件来初始化Settings节目。
  2. AndroidL中舍弃了Header类,取而代之的是DashboardCategory和DashboardTile类。

UI修改

可以看出,如果我们想要改变Settings的UI,那我们主要需要改动如下几个自定义View和布局文件,分别是:

自定义view:

  1. DashboardContainerView
  2. DashboardTileView

布局文件:

  1. dashboard.xml,全局的xml布局文件。
  2. dashboard_category.xml,乘放tile的布局文件。
  3. dashboard_tile.xml,每个tile的布局文件。

你可能感兴趣的:(android)