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 檔案包含要顯示的首選項的靜態清單。
動態偏好
動態項目代表具有意圖的圖塊,導致外部或內部活動。通常,意圖會導致不同的設定頁面。例如,設定主頁中的「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 中的靜態移動
- 尋找原始頁面和目標頁面的首選項 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
的 Activity 元資料值,將該值設定為新頁面的類別鍵。
在頁面中建立新首選項
如果首選項在原始頁面的首選項 XML 檔案中靜態列出,請按照下列靜態程序進行操作。否則遵循動態過程。
建立靜態首選項
- 尋找頁面的首選項 XML 檔案。您可以從頁面的 getPreferenceScreenResId() 方法找到此資訊。
- 在 XML 中新增的首選項項目。確保它有一個唯一的
android:key
。 - 在頁面的
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"/>
- 在 Android 8.x 和 Android 9 中(可選),在頁面的
建立動態偏好
- 尋找原始頁面和目標頁面所在的類別。您可以在
DashboardFragmentRegistry
中找到此資訊。 - 在
AndroidManifest
中建立一個新的Activity - 將必要的元資料新增至新活動以定義設定。將
com.android.settings.category
的元資料值設定為步驟 1 定義的相同值。
建立一個新頁面
- 創建一個新片段,繼承自
DashboardFragment
。 - 在
DashboardFragmentRegistry
中定義其類別。注意:此步驟是可選的。如果您不需要此頁面中的任何動態首選項,則無需提供類別鍵。
- 請依照步驟新增此頁面所需的設定。有關更多信息,請參閱實施部分。
驗證
- 在「設定」中執行 robolectric 測試。所有現有的和新的測試都應該通過。
- 建置並安裝Settings,然後手動開啟正在修改的頁面。該頁面應立即更新。