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
- DashboardFragmentRegistry :
packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
- DashboardFragment :
packages/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 中的靜態移動
- 查找原始頁面和目標頁面的首選項 XML 文件。您可以從頁面的
getPreferenceScreenResId()
方法中找到此信息。 - 從原始頁面的 XML 中刪除首選項。
- 將首選項添加到目標頁面的 XML。
- 從原始頁面的 Java 實現中刪除此首選項的
PreferenceController
。通常它在createPreferenceControllers()
中。控制器可以直接在 XML 中聲明。注意:首選項可能沒有
PreferenceController
。 - 在目標頁面的
createPreferenceControllers()
中實例化PreferenceController
。如果PreferenceController
在舊頁面中的 XML 中定義,那麼也為新頁面在 XML 中定義它。
Android 9 中的動態移動
- 查找原始頁面和目標頁面託管的類別。您可以在
DashboardFragmentRegistry
中找到此信息。 - 打開包含您需要移動的設置的
AndroidManifest.xml
文件並找到代表此設置的 Activity 條目。 - 將
com.android.settings.category
的活動元數據值設置為新頁面的類別鍵。
Android 8.x 版本中的靜態移動
- 查找原始頁面和目標頁面的首選項 XML 文件。 您可以從頁面的
- 刪除原始頁面 XML 中的首選項。
- 將首選項添加到目標頁面的 XML。
- 在原始頁面的 Java 實現中刪除此首選項的
PreferenceController
。通常它在getPreferenceControllers()
中。 - 在目標頁面的
getPreferenceControllers()
中實例化PreferenceController
。
getPreferenceScreenResId()
方法中找到此信息。注意:首選項可能沒有PreferenceController
。
Android 8.x 版本中的動態移動
- 查找原始頁面和目標頁面託管的類別。您可以在
DashboardFragmentRegistry
中找到此信息。 - 打開包含您需要移動的設置的
AndroidManifest.xml
文件並找到代表此設置的 Activity 條目。 - 更改
com.android.settings.category
的活動元數據值,將值點設置為新頁面的類別鍵。
在頁面中創建新首選項
如果首選項在原始頁面的首選項 XML 文件中靜態列出,請遵循以下靜態過程。否則遵循動態過程。
創建靜態首選項
- 查找頁面的首選項 XML 文件。您可以從頁面的 getPreferenceScreenResId() 方法中找到此信息。
- 在 XML 中添加一個新的 Preference 項。確保它具有唯一的
android:key
。 - 在頁面的
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"/>
- 在 Android 8.x 和可選的 Android 9 中,在頁面的
創建動態首選項
- 查找原始頁面和目標頁面託管的類別。您可以在
DashboardFragmentRegistry
中找到此信息。 - 在
AndroidManifest
中創建一個新的 Activity - 將必要的元數據添加到新 Activity 以定義設置。將
com.android.settings.category
的元數據值設置為與步驟 1 中定義的值相同的值。
創建一個新頁面
- 創建一個新的片段,繼承自
DashboardFragment
。 - 在
DashboardFragmentRegistry
中定義其類別。注意:此步驟是可選的。如果您不需要此頁面中的任何動態首選項,則無需提供類別鍵。
- 按照步驟添加此頁面所需的設置。有關詳細信息,請參閱實施部分。
驗證
- 在“設置”中運行 robolectric 測試。所有現有的和新的測試都應該通過。
- 構建並安裝設置,然後手動打開正在修改的頁面。該頁面應立即更新。