資訊架構

Android 8.0 為「設定」應用程式導入了新的資訊架構,簡化設定的整理方式,讓使用者更輕鬆快速地找到設定,自訂 Android 裝置。 Android 9 導入了多項改良功能,提供更多「設定」功能,並簡化實作程序。

範例和來源

目前「設定」中的大多數頁面都是使用新架構實作。以 DisplaySettings 為例: packages/apps/Settings/src/com/android/settings/DisplaySettings.java

重要元件的檔案路徑如下:

  • CategoryKeypackages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
  • DashboardFragmentRegistrypackages/apps/Settings/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
  • DashboardFragmentpackages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
  • AbstractPreferenceControllerframeworks/base/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
  • BasePreferenceController (Android 9 中導入): packages/apps/Settings/src/com/android/settings/core/BasePreferenceController.java

實作

建議裝置製造商採用現有的「設定」資訊架構,並視需要插入其他設定頁面,以配合合作夥伴專屬功能。將偏好設定從舊版頁面 (實作方式為 SettingsPreferencePage) 移至新頁面 (實作方式為 DashboardFragment) 可能很複雜。舊版網頁的偏好設定可能不是以 PreferenceController 實作。

因此,將偏好設定從舊版頁面移至新頁面時,您需要建立 PreferenceController,並在新的 DashboardFragment 中例項化之前,將程式碼移至控制器。PreferenceController 需要的 API 會在其名稱中說明,並記錄在 Javadoc 中。

強烈建議您為每個 PreferenceController 新增單元測試。如果變更已提交至 Android 開放原始碼計畫,則必須進行單元測試。 如要進一步瞭解如何編寫以 Robolectric 為基礎的測試,請參閱 Readme 檔案 packages/apps/Settings/tests/robotests/README.md

外掛程式風格的資訊架構

每個設定項目都會實作為偏好設定。偏好設定可輕鬆從一個頁面移到另一個頁面。

為方便移動多項設定,Android 8.0 導入了包含設定項目的外掛程式樣式主機片段。設定項目會以外掛程式樣式的控制器建模。因此,設定頁面是由單一主機片段和多個設定控制器建構而成。

DashboardFragment

DashboardFragment 是外掛程式樣式偏好設定控制器的主機。 這個片段會從 PreferenceFragment 繼承,並提供擴展和更新靜態偏好設定清單和動態偏好設定清單的掛鉤。

靜態偏好設定

靜態偏好設定清單是使用 <Preference> 標記在 XML 中定義。DashboardFragment 實作項目會使用 getPreferenceScreenResId() 方法,定義要顯示靜態偏好設定清單的 XML 檔案。

動態偏好設定

動態項目代表具有意圖的資訊方塊,可導向外部或內部活動。意圖通常會導向其他設定頁面。舉例來說,設定首頁中的「Google」設定項目是動態項目。動態項目定義於 AndroidManifest (詳見下文),並透過 FeatureProvider (定義為 DashboardFeatureProvider) 載入。

動態設定比靜態設定更耗用資源,因此開發人員通常應將設定實作為靜態設定。不過,在下列任一情況下,動態設定可能很有用:

  • 設定並非直接在「設定」應用程式中實作 (例如插入由 OEM/電信業者應用程式實作的設定)。
  • 設定應會顯示在「設定」首頁。
  • 您已擁有設定的 Activity,且不想實作額外的靜態設定。

如要將「活動」設為動態設定,請按照下列步驟操作:

  • 在活動中新增意圖篩選器,將活動標示為動態設定。
  • 向「設定」應用程式說明該類別的用途。類別是 CategoryKey 中定義的常數。
  • (選用) 在顯示設定時新增摘要文字。

以下範例取自「設定」應用程式的 DisplaySettings

<activity android:name="Settings$DisplaySettingsActivity"
                   android:label="@string/display_settings"
                   android:icon="@drawable/ic_settings_display">
             <!-- Mark the activity as a dynamic setting -->
              <intent-filter>
                     <action android:name="com.android.settings.action.IA_SETTINGS" />
              </intent-filter>
             <!-- Tell Settings app which category it belongs to -->
              <meta-data android:name="com.android.settings.category"
                     android:value="com.android.settings.category.ia.homepage" />
             <!-- Add a summary text when the setting is displayed -->
              <meta-data android:name="com.android.settings.summary"
                     android:resource="@string/display_dashboard_summary"/>
             </activity>

在算繪時,片段會要求提供靜態 XML 和 AndroidManifest 中定義的動態設定,兩者皆為偏好設定清單。無論 PreferenceController 是在 Java 程式碼中定義,還是以 XML 定義,DashboardFragment 都會透過 PreferenceController 管理各項設定的處理邏輯 (詳見下文)。然後在使用者介面中顯示為混合清單。

PreferenceController

在 Android 9 和 Android 8.x 中實作 PreferenceController 的方式有所不同,詳情請參閱本節。

Android 9 版本中的 PreferenceController

PreferenceController 包含與偏好設定互動的所有邏輯,包括顯示、更新、搜尋索引等。

PreferenceController 的介面定義為 BasePreferenceController。舉例來說,請參閱 packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java 中的程式碼

BasePreferenceController 有多個子類別,每個子類別都會對應至「設定」應用程式預設支援的特定 UI 樣式。舉例來說,TogglePreferenceController 的 API 會直接對應使用者與切換式偏好設定 UI 的互動方式。

BasePreferenceController 具有 getAvailabilityStatus()displayPreference()handlePreferenceTreeClicked(), 等 API。每個 API 的詳細說明文件都位於介面類別中。

實作 BasePreferenceController (和 TogglePreferenceController 等子類別) 的限制是建構函式簽章必須符合下列任一條件:

  • public MyController(Context context, String key) {}
  • public MyController(Context context) {}

將偏好設定安裝至片段時,資訊主頁會提供方法,在顯示時間前附加 PreferenceController。安裝時,控制器會連線至片段,因此日後所有相關事件都會傳送至控制器。

DashboardFragment 會在畫面上保留 PreferenceController 清單。在片段的 onCreate() 中,系統會為 getAvailabilityStatus() 方法叫用所有控制器,如果傳回 true,則會叫用 displayPreference() 來處理顯示邏輯。getAvailabilityStatus() 也很重要,可告知設定架構搜尋期間可用的項目。

Android 8.x 版本中的 PreferenceController

PreferenceController 包含與偏好設定互動的所有邏輯,包括顯示、更新、搜尋索引等。

對應偏好設定互動, PreferenceController 的介面具有 isAvailable() displayPreference()handlePreferenceTreeClicked() 等 API。如要瞭解各個 API 的詳細說明文件,請參閱介面類別。

將偏好設定安裝至片段時,資訊主頁會提供方法,在顯示時間前附加 PreferenceController。安裝時,控制器會連線至片段,因此日後所有相關事件都會傳送至控制器。

DashboardFragment 會在畫面上保留 PreferenceControllers 清單。在片段的 onCreate() 中,系統會針對 isAvailable() 方法叫用所有控制器,如果該方法傳回 true,系統就會叫用 displayPreference() 來處理顯示邏輯。

使用 DashboardFragment

將偏好設定從 A 頁面移至 B 頁面

如果偏好設定靜態列於原始頁面的偏好設定 XML 檔案中,請按照下方 Android 版本適用的靜態移動程序操作。否則,請按照 Android 版本的動態遷移程序操作。

Android 9 中的靜態移動

  1. 找出原始網頁和目標網頁的偏好設定 XML 檔案。您可以使用頁面的 getPreferenceScreenResId() 方法取得這項資訊。
  2. 從原始網頁的 XML 中移除偏好設定。
  3. 將偏好設定新增至目的地網頁的 XML。
  4. 從原始頁面的 Java 實作中移除這個偏好設定的 PreferenceController。通常位於 createPreferenceControllers()。控制器可以直接在 XML 中宣告。

    注意:偏好設定可能沒有 PreferenceController

  5. 在到達網頁的 createPreferenceControllers() 中,例項化 PreferenceController。如果舊網頁的 XML 中定義了 PreferenceController,請在新網頁的 XML 中也定義該項目。

Android 9 的動態移動

  1. 找出原始網頁和目標網頁所屬的類別。你可以在DashboardFragmentRegistry中找到這項資訊。
  2. 開啟包含要移動設定的 AndroidManifest.xml 檔案,然後找出代表這項設定的活動項目。
  3. com.android.settings.category 的活動中繼資料值設為新網頁的類別鍵。

Android 8.x 版本中的靜態移動

  1. 找出原始網頁和目的地網頁的偏好設定 XML 檔案。
  2. 您可以透過頁面的 getPreferenceScreenResId() 方法取得這項資訊。
  3. 在原始網頁的 XML 中移除偏好設定。
  4. 將偏好設定新增至目的地網頁的 XML。
  5. 在原始頁面的 Java 實作中,移除這項偏好設定的 PreferenceController。通常位於 getPreferenceControllers()
  6. 注意:偏好設定可能沒有 PreferenceController

  7. 在到達網頁的 getPreferenceControllers() 中,例項化 PreferenceController

Android 8.x 版本中的動態移動

  1. 找出原始網頁和目標網頁所屬的類別。您可以在DashboardFragmentRegistry中找到這項資訊。
  2. 開啟包含要移動設定的 AndroidManifest.xml 檔案,然後找出代表這項設定的活動項目。
  3. 變更活動的 com.android.settings.category 中繼資料值,將值點設為新網頁的類別鍵。

在頁面中建立新的偏好設定

如果偏好設定靜態列於原始頁面的偏好設定 XML 檔案中,請按照下方的靜態程序操作。否則請按照動態程序操作。

建立靜態偏好設定

  1. 找出網頁的偏好設定 XML 檔案。您可以透過頁面的 getPreferenceScreenResId() 方法找到這項資訊。
  2. 在 XML 中新增 Preference 項目。確認該項目有專屬的 android:key
  3. 在頁面的 getPreferenceControllers() 方法中,為這項偏好設定定義 PreferenceController
    • 在 Android 8.x 和 Android 9 (選用) 中,請在網頁的 createPreferenceControllers() 方法中,為這項偏好設定例項化 PreferenceController

      如果其他地方已存在這項偏好設定,可能已有PreferenceController。您可以重複使用 PreferenceController,不必重新建構。

    • 從 Android 9 開始,您可以選擇在偏好設定旁的 XML 中宣告 PreferenceController。例如:
      <Preference
              android:key="reset_dashboard"
              android:title="@string/reset_dashboard_title"
              settings:controller="com.android.settings.system.ResetPreferenceController"/>

建立動態偏好設定

  1. 找出原始網頁和目標網頁所屬的類別。您可以在DashboardFragmentRegistry中找到這項資訊。
  2. 在「AndroidManifest」中建立新活動
  3. 在新活動中加入必要的中繼資料,定義設定。將 com.android.settings.category 的中繼資料值設為與步驟 1 中定義的值相同。

建立新專頁

  1. 建立新的片段,並從 DashboardFragment 繼承。
  2. DashboardFragmentRegistry 中定義類別。

    注意:此為選用步驟。如果這個頁面不需要任何動態偏好設定,您就不必提供類別鍵。

  3. 按照步驟新增這個頁面所需的設定。詳情請參閱「導入」一節。

驗證

  • 在「設定」中執行 Robolectric 測試。所有現有和新的測試都應通過。
  • 建構並安裝「設定」,然後手動開啟要修改的頁面。 頁面應該會立即更新。