資訊架構

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/電信業者應用程式實作的設定)。
  • 設定應會顯示在「設定」首頁。
  • 設定中已有「活動」設定,且不想實作額外靜態設定。

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

  • 將意圖篩選器新增至活動,將活動標示為動態設定。
  • 告訴「設定」應用程式所屬類別。類別是 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>

在算繪期間,片段會要求 AndroidManifest 中定義的靜態 XML 和動態設定的偏好設定清單。無論 PreferenceController 是在 Java 程式碼或 XML 中定義,DashboardFragment 都會透過 PreferenceController 管理每個設定的處理邏輯 (詳見下文)。然後在 UI 中以混合清單的形式顯示。

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。如果 PreferenceController 是在舊頁面的 XML 中定義,請一併在新頁面的 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 中新增偏好設定項目。請確認變數均不重複 android:key
  3. 在頁面的 getPreferenceControllers() 方法中,為此偏好設定定義 PreferenceController
    • 在 Android 8.x 和選用的 Android 9 中,請在頁面的 createPreferenceControllers() 方法中為此偏好設定例項化 PreferenceController

      如果這個偏好設定已存在於其他位置,則可能已為其建立 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 測試。所有現有和新測試都應通過。
  • 建構並安裝設定,然後手動開啟要修改的頁面。頁面應該會立即更新。