Android 8.0 introdujo una nueva arquitectura de información para la aplicación Configuración para simplificar la forma en que se organizan las configuraciones y facilitar a los usuarios encontrar rápidamente configuraciones para personalizar sus dispositivos Android. Android 9 introdujo algunas mejoras para proporcionar más funciones de configuración y una implementación más sencilla.
Ejemplos y fuente
La mayoría de las páginas de Configuración se implementan actualmente utilizando el nuevo marco. Un buen ejemplo es DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java
Las rutas de archivos para componentes importantes se enumeran a continuación:
- Clave de categoría :
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 (introducido en Android 9):
packages/apps/Settings/src/com/android/settings/core/BasePreferenceController.java
Implementación
Se anima a los fabricantes de dispositivos a adaptar la arquitectura de información de configuración existente e insertar páginas de configuración adicionales según sea necesario para adaptarse a funciones específicas de los socios. Mover las preferencias de una página heredada (implementada como SettingsPreferencePage
) a una nueva página (implementada usando DashboardFragment
) puede resultar complicado. Es probable que la preferencia de la página heredada no se implemente con PreferenceController
.
Entonces, al mover preferencias de una página heredada a una página nueva, debe crear un PreferenceController
y mover el código al controlador antes de crear una instancia en el nuevo DashboardFragment
. Las API que requiere PreferenceController
se describen en su nombre y están documentadas en Javadoc.
Se recomienda encarecidamente agregar una prueba unitaria para cada PreferenceController
. Si el cambio se envía a AOSP, se requiere una prueba unitaria. Para obtener más información sobre cómo escribir pruebas basadas en Robolectric, consulte el archivo Léame packages/apps/Settings/tests/robotests/README.md
.
Arquitectura de información estilo complemento
Cada elemento de configuración se implementa como una preferencia. Una preferencia se puede mover fácilmente de una página a otra.
Para facilitar el movimiento de múltiples configuraciones, Android 8.0 introdujo un fragmento de host estilo complemento que contiene elementos de configuración. Los elementos de configuración se modelan como controladores estilo complemento. Por lo tanto, una página de configuración se construye mediante un único fragmento de host y múltiples controladores de configuración.
Fragmento del tablero
DashboardFragment
es el anfitrión de controladores de preferencias estilo complemento. El fragmento hereda de PreferenceFragment
y tiene enlaces para expandir y actualizar tanto las listas de preferencias estáticas como las listas de preferencias dinámicas.
Preferencias estáticas
Una lista de preferencias estáticas se define en XML utilizando la etiqueta <Preference>
. Una implementación DashboardFragment
utiliza el método getPreferenceScreenResId()
para definir qué archivo XML contiene la lista estática de preferencias para mostrar.
Preferencias dinámicas
Un elemento dinámico representa un mosaico con intención que conduce a una actividad externa o interna. Por lo general, la intención conduce a una página de configuración diferente. Por ejemplo, el elemento de configuración "Google" en la página de inicio de Configuración es un elemento dinámico. Los elementos dinámicos se definen en AndroidManifest
(que se analiza a continuación) y se cargan a través de un FeatureProvider
(definido como DashboardFeatureProvider
).
Las configuraciones dinámicas son más pesadas que las configuraciones configuradas estáticamente, por lo que normalmente los desarrolladores deberían implementar la configuración como estática. Sin embargo, la configuración dinámica puede resultar útil cuando se cumple alguna de las siguientes condiciones:
- La configuración no se implementa directamente en la aplicación Configuración (como inyectar una configuración implementada por aplicaciones OEM/Operador).
- La configuración debería aparecer en la página de inicio de Configuración.
- Ya tiene una Actividad para la configuración y no desea implementar la configuración estática adicional.
Para configurar una Actividad como una configuración dinámica, haga lo siguiente:
- Marque la actividad como una configuración dinámica agregando un filtro de intención a la actividad.
- Dígale a la aplicación Configuración a qué categoría pertenece. La categoría es una constante, definida en
CategoryKey
. - Opcional: agregue texto de resumen cuando se muestre la configuración.
Aquí hay un ejemplo tomado de la aplicación Configuración para 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>
En el momento de la renderización, el fragmento solicitará una lista de Preferencias tanto de XML estático como de configuraciones dinámicas definidas en AndroidManifest
. Ya sea que los PreferenceController
estén definidos en código Java o en XML, DashboardFragment
administra la lógica de manejo de cada configuración a través de PreferenceController
(que se analiza a continuación). Luego se muestran en la interfaz de usuario como una lista mixta.
Controlador de preferencias
Existen diferencias entre implementar PreferenceController
en Android 9 y Android 8.x, como se describe en esta sección.
PreferenceController en la versión de Android 9
Un PreferenceController
contiene toda la lógica para interactuar con la preferencia, incluida la visualización, actualización, indexación de búsqueda, etc.
La interfaz de PreferenceController
se define como BasePreferenceController
. Por ejemplo, consulte el código en packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java
Hay varias subclases de BasePreferenceController
, cada una de las cuales se asigna a un estilo de interfaz de usuario específico que la aplicación Configuración admite de forma predeterminada. Por ejemplo, TogglePreferenceController
tiene una API que asigna directamente cómo el usuario debe interactuar con una interfaz de usuario de preferencias basada en alternancia.
BasePreferenceController
tiene API como getAvailabilityStatus()
, displayPreference()
, handlePreferenceTreeClicked(),
etc. La documentación detallada de cada API se encuentra en la clase de interfaz.
Una restricción en la implementación BasePreferenceController
(y sus subclases como TogglePreferenceController
) es que la firma del constructor debe coincidir con cualquiera de los siguientes:
-
public MyController(Context context, String key) {}
-
public MyController(Context context) {}
Al instalar una preferencia en el fragmento, el panel proporciona un método para adjuntar un PreferenceController
antes del momento de visualización. En el momento de la instalación, el controlador se conecta al fragmento para que todos los eventos relevantes futuros se envíen al controlador.
DashboardFragment
mantiene una lista de PreferenceController
en la pantalla. En onCreate()
del fragmento, se invocan todos los controladores para el método getAvailabilityStatus()
y, si devuelve verdadero, se invoca displayPreference()
para procesar la lógica de visualización. getAvailabilityStatus()
también es importante para indicarle al marco de configuración qué elementos están disponibles durante la búsqueda. PreferenceController en las versiones de Android 8.x
Un PreferenceController
contiene toda la lógica para interactuar con la preferencia, incluida la visualización, actualización e indexación de búsqueda. etc.
En correspondencia con las interacciones de preferencias, la interfaz de PreferenceController
tiene API isAvailable()
, displayPreference()
, handlePreferenceTreeClicked()
etc. Se puede encontrar documentación detallada sobre cada API en la clase de interfaz.
Al instalar una preferencia en el fragmento, el panel proporciona un método para adjuntar un PreferenceController
antes del momento de visualización. En el momento de la instalación, el controlador se conecta al fragmento para que todos los eventos relevantes futuros se envíen al controlador.
DashboardFragment
mantiene una lista de PreferenceControllers
en la pantalla. En onCreate()
del fragmento, se invocan todos los controladores para el método isAvailable()
y, si devuelve verdadero, se invoca displayPreference()
para procesar la lógica de visualización.
Usando DashboardFragment
Mover una preferencia de la página A a la B
Si la preferencia aparece estáticamente en el archivo XML de preferencias de la página original, siga el procedimiento de movimiento estático para su versión de Android a continuación. De lo contrario, siga el procedimiento de movimiento dinámico para su versión de Android.
Movimiento estático en Android 9
- Busque los archivos XML de preferencias para la página original y la página de destino. Puede encontrar esta información en el método
getPreferenceScreenResId()
de la página. - Elimine la preferencia del XML de la página original.
- Agregue la preferencia al XML de la página de destino.
- Elimine el
PreferenceController
para esta preferencia de la implementación Java de la página original. Generalmente está encreatePreferenceControllers()
. El controlador podría declararse directamente en XML.Nota : Es posible que la preferencia no tenga un
PreferenceController
. - Cree una instancia de
PreferenceController
encreatePreferenceControllers()
de la página de destino. SiPreferenceController
está definido en XML en la página anterior, defínalo también en XML para la nueva página.
Movimiento dinámico en Android 9
- Encuentre qué categoría alberga la página original y la de destino. Puede encontrar esta información en
DashboardFragmentRegistry
. - Abra el archivo
AndroidManifest.xml
que contiene la configuración que necesita mover y busque la entrada Actividad que representa esta configuración. - Establezca el valor de metadatos de la actividad para
com.android.settings.category
en la clave de categoría de la nueva página.
Movimiento estático en las versiones de Android 8.x
- Busque los archivos XML de preferencias para la página original y la página de destino. Puede encontrar esta información en el método
- Elimine la preferencia en el XML de la página original.
- Agregue la preferencia al XML de la página de destino.
- Elimine
PreferenceController
para esta preferencia en la implementación de Java de la página original. Por lo general, está engetPreferenceControllers()
. - Cree una instancia de
PreferenceController
engetPreferenceControllers()
de la página de destino.
getPreferenceScreenResId()
de la página. Nota : Es posible que la preferencia no tenga un PreferenceController
.
Movimiento dinámico en las versiones de Android 8.x
- Encuentre qué categoría alberga la página original y la de destino. Puede encontrar esta información en
DashboardFragmentRegistry
. - Abra el archivo
AndroidManifest.xml
que contiene la configuración que necesita mover y busque la entrada Actividad que representa esta configuración. - Cambie el valor de metadatos de la actividad para
com.android.settings.category
y establezca el valor en la clave de categoría de la nueva página.
Crear una nueva preferencia en una página
Si la preferencia aparece estáticamente en el archivo XML de preferencias de la página original, siga el procedimiento estático siguiente. En caso contrario seguir el procedimiento dinámico .
Creando una preferencia estática
- Busque los archivos XML de preferencias para la página. Puede encontrar esta información en el método getPreferenceScreenResId() de la página.
- Agregue un nuevo elemento de preferencia en el XML. Asegúrese de que tenga una
android:key
única. - Defina un
PreferenceController
para esta preferencia en el métodogetPreferenceControllers()
de la página.- En Android 8.x y, opcionalmente, en Android 9, crea una instancia de un
PreferenceController
para esta preferencia en el métodocreatePreferenceControllers()
de la página.Si esta preferencia ya existía en otros lugares, es posible que ya exista un
PreferenceController
para ella. Puede reutilizarPreferenceController
sin crear uno nuevo. - A partir de Android 9, puedes optar por declarar
PreferenceController
en XML junto a la preferencia. Por ejemplo:<Preference android:key="reset_dashboard" android:title="@string/reset_dashboard_title" settings:controller="com.android.settings.system.ResetPreferenceController"/>
- En Android 8.x y, opcionalmente, en Android 9, crea una instancia de un
Creando una preferencia dinámica
- Encuentre qué categoría alberga la página original y la de destino. Puede encontrar esta información en
DashboardFragmentRegistry
. - Crear una nueva actividad en
AndroidManifest
- Agregue los metadatos necesarios a la nueva Actividad para definir la configuración. Establezca el valor de metadatos para
com.android.settings.category
en el mismo valor definido en el paso 1.
Crear una nueva página
- Cree un nuevo fragmento, heredando de
DashboardFragment
. - Defina su categoría en
DashboardFragmentRegistry
.Nota: Este paso es opcional. Si no necesita ninguna preferencia dinámica en esta página, no necesita proporcionar una clave de categoría.
- Siga los pasos para agregar las configuraciones necesarias para esta página. Para obtener más información, consulte la sección Implementación .
Validación
- Ejecute las pruebas roboeléctricas en Configuración. Todas las pruebas nuevas y existentes deben pasar.
- Cree e instale Configuración, luego abra manualmente la página que se está modificando. La página debería actualizarse inmediatamente.