android O版本 設置(Settings)模塊總結--設置的啓動界面選擇

之前一直在公司做設置模塊的功能修改,現在轉到了其它模塊,所以就總結一下吧,有不足之處,還請指正.

設置是整個android系統的重要應用,涉及的都是系統功能,本文只是對其結構進行解析,功能控制等不做說明

(1)設置的界面選擇

設置裏面的Activity乍一看和常規應用的Activity有很大不同,但是原理都是一樣的,不過設置爲了更方便的區分和獲取信息,在AndroidManifest.xml文件中添加了許多的屬性,這可能導致很多人看着頭疼,但是這也是設置的精髓所在.

 

設置的啓動界面: O/packages/apps/Settings/src/com/android/settings/Settings.java

Settings的父類是SettingsActivity,而且其中有很多繼承SettingsActivity的內部類,這個地方是很有意思,後續再說,先看下SettingsActivity.java

O/packages/apps/Settings/src/com/android/settings/SettingsActivity.java

SettingsActivity的父類是SettingsDrawerActivity,而SettingsDrawerActivity是在SettingLib中定義

O/frameworks/base/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java

SettingsDrawerActivity名稱是沿用N版本的名字,但是側滑欄功能在O版本上已經移除了,google在這個地方偷了下懶.

這父子倆的onCreate():

  SettingsDrawerActivity
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //主要是佈局設置
        super.setContentView(R.layout.settings_with_drawer);
        mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container);

        Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
        if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
            toolbar.setVisibility(View.GONE);
            return;
        }
        setActionBar(toolbar);
    }


SettingsActivity
    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
       ...... 
        //獲取的類名,此處獲取的可能是在Settings.java中的內部類,或者就是Settings.java

        // Should happen before any call to getIntent()
        getMetaData();
        // Getting Intent properties can only be done after the super.onCreate(...)
        final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
        ......
        final ComponentName cn = intent.getComponent();
        final String className = cn.getClassName();
        //mIsShowingDashboard會根據不同的類名佈置不同的佈局
        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) {
            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);
       ......
        //跳轉到制定的界面
        launchSettingFragment(initialFragmentName, isSubSettings, intent);

       ......
    }

在SettingsActivity的onCreate中去其實會有三個不同佈局的加載方向,以下是重點了:

Settings , Settings的內部類 , SubSettings

(a)Settings ---主界面(一級界面)

onCreate函數中會根據類名加載不同的佈局,而布爾值:mIsShowingDashboard 就是當前是否爲一級界面的判斷

        mIsShowingDashboard = className.equals(Settings.class.getName());
        setContentView(mIsShowingDashboard ?
                R.layout.settings_main_dashboard : R.layout.settings_main_prefs);

 

settings_main_dashboard.xml就很簡單了只是一個FrameLayout

 

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/main_content"
             android:layout_height="match_parent"
             android:layout_width="match_parent"
             />

在之後的界面跳轉即 launchSettingFragment(initialFragmentName, isSubSettings, intent)函數中又做出了區分:

    void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
        if (!mIsShowingDashboard && initialFragmentName != null) {
            ......
        } else {
            // No UP affordance if we are displaying the main Dashboard
            mDisplayHomeAsUpEnabled = false;
            // Show Search affordance
            mDisplaySearch = true;
            mInitialTitleResId = R.string.dashboard_title;
            //此處是真正顯示一級界面的操作
            switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
                mInitialTitleResId, mInitialTitle, false);
        }
    }

在launchSettingFragment函數中正式加載了設置的一級界面DashboardSummary.java,DashboardSummary是一個Fragment,而內部佈局是使用的R.layout.dashboard,dashboard.xml包含了一個自定義的RecyclerView類FocusRecyclerView,此處就不再詳細說明了.

(b)Settings的內部類

Settings的內部類的啓動一版都是通過activity 中的action屬性啓動的,而判斷的依據也是通過mIsShowingDashboard,加載的佈局爲R.layout.settings_main_prefs,而settings_main_prefs.xml比dashboard.xml來說就增加了一個switchBar:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_height="match_parent"
              android:layout_width="match_parent">

    <LinearLayout
            android:orientation="vertical"
            android:layout_height="0px"
            android:layout_width="match_parent"
            android:layout_weight="1">

        <com.android.settings.widget.SwitchBar android:id="@+id/switch_bar"
                  android:layout_height="?android:attr/actionBarSize"
                  android:layout_width="match_parent"
                  android:background="@drawable/switchbar_background"
                  android:theme="?attr/switchBarTheme"
                />

        <FrameLayout
                android:id="@+id/main_content"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                />

    </LinearLayout>

    <RelativeLayout /><!--沒怎麼研究過,此處代碼就省略了-->

</LinearLayout>

Settings的內部類的主體顯示內容依然是一個Fragment,而這個Fragment已經在AndroidManifest.xml中定義好了.拿StorageDashboardActivity爲例:

        <activity android:name=".Settings$StorageDashboardActivity"
                android:label="@string/storage_settings"
                android:icon="@drawable/ic_settings_storage"
                android:taskAffinity="com.android.settings"
                android:parentActivityName="Settings">
            <intent-filter android:priority="1">
                <action android:name="android.settings.INTERNAL_STORAGE_SETTINGS" />
                <action android:name="android.settings.MEMORY_CARD_SETTINGS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.VOICE_LAUNCH" />
            </intent-filter>
            <intent-filter android:priority="5">
                <action android:name="com.android.settings.action.SETTINGS" />
            </intent-filter>
            <meta-data android:name="com.android.settings.category"
                android:value="com.android.settings.category.ia.homepage" />
            <meta-data android:name="com.android.settings.title"
                android:resource="@string/storage_usb_settings" />
            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                android:value="com.android.settings.deviceinfo.StorageSettings" />
            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                android:value="true" />
        </activity>

這個地方很容易會讓人迷糊的,主要是裏面的屬性太多,看第一眼估計就會暈掉,這些屬性是在SettingLib中的O/frameworks/base/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java有用到的,後續有時間的話可以總結一下,TileUtils主要目錄加載的工具類.

言歸正傳,內部類StorageDashboardActivity的Fragment顯示內容爲:com.android.settings.FRAGMENT_CLASS,即com.android.settings.deviceinfo.StorageSettings
(c)SubSettings

和內部類就很相似了,不過是做了主題的切換:

        if (isSubSettings) {
            setTheme(R.style.Theme_SubSettings);
        }

SubSettings的啓動方式是很隱蔽的這裏把啓動的順序貼下,各位有時間的話可以自己研究一下:

android.support.v7.preference.Preference$1.onClick()
android.support.v7.preference.Preference.performClick()
com.android.settings.fuelgauge.PowerUsageSummary.onPreferenceTreeClick()
com.android.settings.dashboard.DashboardFragment.onPreferenceTreeClick()
android.support.v14.preference.PreferenceFragment.onPreferenceTreeClick()
com.android.settings.SettingsActivity.onPreferenceStartFragment()
com.android.settings.SettingsActivity.startPreferencePanel()
com.android.settings.Utils.startWithFragment()
com.android.settings.Utils.onBuildStartFragmentIntent()

 

到這裏爲止,設置的界面加載啓動基本上已經總結完了,看起來還是很簡單的,但是這只是啓動而已,內部的數據加載,數據與View的綁定其實還有很多東西,後續有時間的話會繼續總結.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章