資訊架構

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 為基礎的測試,請參閱讀取檔案 packages/apps/Settings/tests/robotests/README.md

外掛程式樣式資訊架構

每個設定項目都會做為偏好設定。您可以在不同頁面之間輕鬆調整偏好設定。

為方便您移動多項設定,Android 8.0 推出了一個包含設定項目的外掛程式樣式主機片段。系統會將設定項目建模為外掛程式樣式控制器。因此,設定頁面是由單一主機片段和多個設定控制器所建構。

資訊主頁片段

DashboardFragment 是外掛程式樣式的偏好設定控制器主機。片段繼承自 PreferenceFragment,並且具有掛鉤展開及更新靜態偏好設定清單和動態偏好設定清單。

靜態偏好設定

靜態偏好設定清單是在 XML 中使用 <Preference> 標記定義。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 的介麵包含 API isAvailable() displayPreference()handlePreferenceTreeClicked() 等。您可以在介面類別中找到各個 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 檔案,找出代表這項設定的 Activity 項目。
  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 檔案,找出代表這項設定的 Activity 項目。
  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,不必建構新的項目。

    • 從 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 測試。所有現有和新的測試都應通過。
  • 建立及安裝設定,然後手動開啟修改的頁面。 網頁會立即更新。