信息架構

Android 8.0 為“設置”應用引入了新的信息架構,以簡化設置的組織方式,讓用戶更容易快速找到設置以自定義其 Android 設備。 Android 9 引入了一些改進,以提供更多設置功能和更輕鬆的實施。

示例和來源

設置中的大多數頁面當前都是使用新框架實現的。一個很好的例子是 DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java

下面列出了重要組件的文件路徑:

  • CategoryKey : packages/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
  • AbstractPreferenceController : frameworks/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添加單元測試。如果將更改提交給 AOSP,則需要進行單元測試。要獲取有關如何編寫基於 Robolectric 的測試的更多信息,請參閱自述文件packages/apps/Settings/tests/robotests/README.md

插件式信息架構

每個設置項都作為首選項實現。首選項可以很容易地從一個頁面移動到另一個頁面。

為了更容易移動多個設置,Android 8.0 引入了一個包含設置項的插件式主機片段。設置項被建模為插件式控制器。因此,設置頁面由單個主機片段和多個設置控制器構成。

儀表板片段

DashboardFragment是插件式首選項控制器的宿主。該片段繼承自PreferenceFragment並具有擴展和更新靜態首選項列表和動態首選項列表的掛鉤。

靜態首選項

使用<Preference>標記在 XML 中定義靜態首選項列表。 DashboardFragment實現使用getPreferenceScreenResId()方法來定義哪個 XML 文件包含要顯示的首選項的靜態列表。

動態偏好

動態項表示具有意圖的圖塊,導致外部或內部活動。通常,意圖會導致不同的設置頁面。例如,設置首頁中的“谷歌”設置項是動態項。動態項目在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 中顯示為混合列表。

偏好控制器

如本節所述,在 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()來處理顯示邏輯。

使用儀表板片段

將首選項從頁面 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 中添加一個新的 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中創建一個新的 Activity
  3. 將必要的元數據添加到新 Activity 以定義設置。將com.android.settings.category的元數據值設置為與步驟 1 中定義的值相同的值。

創建一個新頁面

  1. 創建一個新的片段,繼承自DashboardFragment
  2. DashboardFragmentRegistry中定義其類別。

    注意:此步驟是可選的。如果您不需要此頁面中的任何動態首選項,則無需提供類別鍵。

  3. 按照步驟添加此頁面所需的設置。有關詳細信息,請參閱實施部分。

驗證

  • 在“設置”中運行 robolectric 測試。所有現有的和新的測試都應該通過。
  • 構建並安裝設置,然後手動打開正在修改的頁面。該頁面應立即更新。