資訊架構

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

插件式資訊架構

每個設定項都作為首選項實現。首選項可以輕鬆地從一頁移動到另一頁。

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

儀表板片段

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 中。

偏好控制器

在 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 的詳細文件位於介面類別中。

實作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包含與首選項互動的所有邏輯,包括顯示、更新、搜尋索引。 ETC。

與偏好互動相對應, 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的 Activity 元資料值,將該值設定為新頁面的類別鍵。

在頁面中建立新首選項

如果首選項在原始頁面的首選項 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中建立一個新的Activity
  3. 將必要的元資料新增至新活動以定義設定。將com.android.settings.category的元資料值設定為步驟 1 定義的相同值。

建立一個新頁面

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

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

  3. 請依照步驟新增此頁面所需的設定。有關更多信息,請參閱實施部分。

驗證

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