Android proporciona todos los ingredientes para desarrollar apps destinadas a pantallas grandes de cinco estrellas. En las recetas de esta guia, se seleccionan y combinan ingredientes de opciones para resolver problemas de desarrollo específicos. Cada receta incluye prácticas recomendadas, muestras de código de calidad y, además, instrucciones paso a paso para ayudarte a convertirte en un gran chef de las pantallas grandes.
Calificaciones por estrellas
Las recetas se califican por estrellas según la medida en que se ajustan a los lineamientos de calidad de apps para pantallas grandes.
Cumple con los criterios del Nivel 1: diferenciada para pantalla grande | |
Cumple con los criterios del Nivel 2: optimizada para pantalla grande | |
Cumple con los criterios del Nivel 3: preparada para pantalla grande | |
Proporciona algunas capacidades para pantallas grandes, pero no cumple con los lineamientos de calidad correspondientes | |
Satisface las necesidades de un caso de uso específico, pero no es compatible con pantallas grandes |
Compatibilidad con la cámara de Chromebook
Haz que los usuarios de Chromebook te vean en Google Play.
Si tu app de cámara solo admite las funciones básicas de cámara, no permitas que las tiendas de aplicaciones eviten que los usuarios de Chromebook instalen la app solo porque especificaste involuntariamente funciones de cámara avanzadas de teléfonos de alta gama.
Las Chromebooks tienen una cámara frontal (orientadas al usuario) integrada que funciona bien para videoconferencias, instantáneas y otras aplicaciones. Sin embargo, no todas las Chromebooks tienen una cámara posterior (orientadas al mundo), y la mayoría de las cámaras orientadas al usuario no admiten el enfoque automático ni el flash.
Prácticas recomendadas
Las apps de cámara versátiles admiten todos los dispositivos, independientemente de la configuración de la cámara: dispositivos con cámaras frontales, cámaras posteriores y cámaras externas conectadas por USB.
Para garantizar que las tiendas de aplicaciones permitan que la app esté disponible para la mayor cantidad de dispositivos posible, declara siempre todas las funciones de cámara que esta use e indica de forma explícita si las funciones son obligatorias o no.
Ingredientes
- Permiso de
CAMERA
: Otorga a tu app acceso a las cámaras de un dispositivo. - Elemento de manifiesto
<uses-feature>
: Informa a las tiendas de aplicaciones sobre las funciones que usa la app. - Atributo
required
: Indica a las tiendas de aplicaciones si tu app puede funcionar sin una función especificada.
Pasos
Resumen
Declara el permiso CAMERA
. Declara las funciones de cámara que proporcionan compatibilidad básica con la cámara. Especifica si cada función es obligatoria o no.
1. Declara el permiso CAMERA
Agrega el siguiente permiso al manifiesto de la app:
<uses-permission android:name="android.permission.CAMERA" />
2. Declara las funciones básicas de la cámara
Agrega las siguientes funciones al manifiesto de la app:
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. Especifica si cada función es obligatoria
Configura android:required="false"
para la función android.hardware.camera.any
para habilitar el acceso a tu app en dispositivos que tengan cualquier tipo de cámara integrada o externa, o que no tengan ninguna cámara.
En el caso de las otras funciones, configura android:required="false"
para garantizar que los dispositivos como las Chromebooks que no tienen cámara posterior, enfoque automático o flash puedan acceder a tu app en tiendas de aplicaciones.
Resultados
Los usuarios de Chromebook pueden descargar e instalar tu app desde Google Play y otras tiendas de aplicaciones. Además, los dispositivos que admiten todas las funciones de la cámara, como los teléfonos, no tendrán restricciones de funcionalidad de cámara.
Si configuras de forma explícita las funciones de cámara compatibles con tu app y especificas las funciones que esta requiere, la app estará disponible para la mayor cantidad posible de dispositivos.
Recursos adicionales
Para obtener más información, consulta Funciones de hardware de la cámara en la documentación de <uses-feature>
.
Orientación de la app restringida en los teléfonos, pero no en los dispositivos con pantalla grande
Tu app funciona muy bien en teléfonos con orientación vertical, por lo que la restringiste a ese modo. Sin embargo, consideras que puedes aprovechar más las pantallas grandes en orientación horizontal.
¿Cómo puedes hacer las dos cosas: restringir la app a la orientación vertical en pantallas pequeñas y habilitar la orientación horizontal en pantallas grandes?
Prácticas recomendadas
Las mejores apps respetan las preferencias de los usuarios, como la orientación del dispositivo.
Los lineamientos de calidad de las apps para pantalla grande recomiendan que estas admitan todas las configuraciones de dispositivos, incluidas las orientaciones vertical y horizontal, el modo multiventana y los estados plegado y desplegado de los dispositivos plegables. Las apps deben optimizar los diseños y las interfaces de usuario para diferentes configuraciones y deben guardar y restablecer el estado durante los cambios de configuración.
Esta receta es una medida temporal: una pizca de compatibilidad con pantallas grandes. Úsala hasta que puedas mejorar tu app para proporcionar compatibilidad total con todas las configuraciones de los dispositivos.
Ingredientes
screenOrientation
: Parámetro de configuración del manifiesto de la app que te permite especificar cómo responde la app a los cambios de orientación del dispositivo- Jetpack WindowManager: Es un conjunto de bibliotecas que te permiten determinar el tamaño y la relación de aspecto de la ventana de la app. retrocompatible con nivel de API 14
Activity#setRequestedOrientation()
: Método con el que puedes cambiar la orientación de la app en el tiempo de ejecución
Pasos
Resumen
Habilita la app para controlar los cambios de orientación de forma predeterminada en el manifiesto de la app. Durante el tiempo de ejecución, determina el tamaño de la ventana de la app. Si la ventana de la app es pequeña, anula la configuración de la orientación del manifiesto para restringir la orientación de la app.
1. Especifica la configuración de orientación en el manifiesto de la app
Puedes evitar declarar el elemento screenOrientation
del manifiesto de la app (en cuyo caso, la orientación se establece de forma predeterminada como unspecified
) o establecer la orientación de la pantalla como fullUser
. Si el usuario no bloqueó la rotación basada en sensores, la app admitirá todas las orientaciones del dispositivo.
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
La diferencia entre unspecified
y fullUser
es sutil, pero importante. Si no declaras un valor de screenOrientation
, el sistema elige la orientación, y la política que usa para definirla puede diferir de un dispositivo a otro. Por otro lado, especificar fullUser
coincide con el comportamiento que el usuario definió para el dispositivo: si el usuario bloqueó la rotación basada en sensores, la app sigue la preferencia del usuario. De lo contrario, el sistema admite cualquiera de las cuatro orientaciones posibles de la pantalla (vertical, horizontal, vertical inverso u horizontal inverso). Consulta android:screenOrientation
.
2. Determina el tamaño de la pantalla
Con el manifiesto configurado para admitir todas las orientaciones permitidas por el usuario, puedes especificar la orientación de la app de manera programática según el tamaño de la pantalla.
Agrega las bibliotecas de Jetpack WindowManager al archivo build.gradle
o build.gradle.kts
del módulo:
Kotlin
implementation("androidx.window:window:version
") implementation("androidx.window:window-core:version
")
Groovy
implementation 'androidx.window:window:version
' implementation 'androidx.window:window-core:version
'
Usa el método WindowMetricsCalculator#computeMaximumWindowMetrics()
de Jetpack WindowManager para obtener el tamaño de pantalla del dispositivo como un objeto WindowMetrics
. Las métricas de ventana se pueden comparar con las clases de tamaño de ventana para decidir cuándo restringir la orientación.
Las clases de tamaño de ventanas proporcionan los puntos de interrupción entre las pantallas pequeñas y las grandes.
Usa los puntos de interrupción WindowWidthSizeClass#COMPACT
y WindowHeightSizeClass#COMPACT
para determinar el tamaño de la pantalla:
Kotlin
/** Determines whether the device has a compact screen. **/ fun compactScreen() : Boolean { val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT }
Java
/** Determines whether the device has a compact screen. **/ private boolean compactScreen() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this); int width = metrics.getBounds().width(); int height = metrics.getBounds().height(); float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density); return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT || windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT; }
- Nota:
- Los ejemplos anteriores se implementan como métodos de una actividad. Por lo tanto, se hace referencia a la actividad como
this
en el argumento decomputeMaximumWindowMetrics()
. - Se usa el método
computeMaximumWindowMetrics()
en lugar decomputeCurrentWindowMetrics()
, ya que la app se puede iniciar en el modo multiventana, que ignora la configuración de orientación de la pantalla. No tiene sentido determinar el tamaño de la ventana de la app y anular la configuración de orientación, a menos que la ventana de la app sea la de todo el dispositivo.
Consulta WindowManager para obtener instrucciones para declarar dependencias y que el método computeMaximumWindowMetrics()
esté disponible en tu app.
3. Anula la configuración del manifiesto de la app
Cuando hayas determinado que el dispositivo tiene un tamaño de pantalla compacta, puedes llamar a Activity#setRequestedOrientation()
para anular el parámetro screenOrientation
del manifiesto:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER } }) }
Java
@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstanceState); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } } }); }
Si agregas la lógica a los métodos onCreate()
y View.onConfigurationChanged()
, puedes obtener las métricas máximas de la ventana y anular la configuración de orientación cada vez que se cambie el tamaño de la actividad o se la mueva entre pantallas, como después de la rotación del dispositivo o cuando un dispositivo plegable se pliega o se despliega.
Para obtener más información sobre cuándo se producen los cambios de configuración y cuándo provocan recreación de actividad, consulta Cómo administrar los cambios en la configuración.
Resultados
Ahora la app debería permanecer en orientación vertical en pantallas pequeñas, independientemente de la rotación del dispositivo. En pantallas grandes, la app debería admitir la orientación horizontal y vertical.
Recursos adicionales
Si necesitas ayuda con la actualización de la app para admitir todas las configuraciones de dispositivos en todo momento, consulta lo siguiente:
- Cómo brindar compatibilidad con diferentes tamaños de pantalla
- Cómo administrar los cambios en la configuración
- Cómo guardar estados de la IU
Cómo pausar y reanudar la reproducción de contenido multimedia con la barra espaciadora del teclado externo
La optimización para pantallas grandes incluye la posibilidad de controlar entradas de teclado externas, como reaccionar cuando se presiona la barra espaciadora para pausar o reanudar la reproducción de videos y otro contenido multimedia. Esto es particularmente útil para tablets, que a menudo se conectan a teclados externos, y Chromebooks, que suelen tener teclados externos, pero que se pueden usar en modo tablet.
Cuando el contenido multimedia sea el único elemento de la ventana (como la reproducción de video en pantalla completa), puede responder a eventos de pulsación de teclas a nivel de actividad o, en Jetpack Compose, a nivel de pantalla.
Prácticas recomendadas
Cada vez que la app reproduce un archivo multimedia, los usuarios deben poder pausar y reanudar la reproducción presionando la barra espaciadora en un teclado físico.
Ingredientes
KEYCODE_SPACE
: Clave de código constante para la barra espaciadora.
Compose
onPreviewKeyEvent
:Modifier
permite que un componente intercepte eventos clave de hardware cuando este (o uno de sus elementos secundarios) esté enfocado.onKeyEvent
: Al igual queonPreviewKeyEvent
, esteModifier
permite que un componente intercepte eventos clave de hardware cuando este (o uno de sus elementos secundarios) esté enfocado.
Views
onKeyUp()
: Se llama cuando se libera una clave y no la maneja una vista dentro de una actividad.
Pasos
Resumen
Las apps basadas en objetos View y las basadas en Jetpack Compose responden a las pulsaciones de teclas del teclado de forma similar: la app debe escuchar los eventos de pulsación de teclas, filtrarlos y responder a las pulsaciones de teclas seleccionadas, como cuando se presiona la barra espaciadora.
1. Cómo escuchar eventos del teclado
Views
En una actividad de tu app, anula el método onKeyUp()
:
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { ... }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { ... }
El método se invoca cada vez que se suelta una tecla presionada, por lo que se activa exactamente una vez por cada vez que se presiona una tecla.
Compose
Con Jetpack Compose, puedes aprovechar el modificador onPreviewKeyEvent
o onKeyEvent
dentro de la pantalla que administra la pulsación de teclas:
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
o
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
2. Cómo filtrar pulsaciones de la barra espaciadora
Dentro del método onKeyUp()
o de los métodos de modificador onPreviewKeyEvent
y onKeyEvent
de Compose, filtra por KeyEvent.KEYCODE_SPACE
para enviar el evento correcto al componente multimedia:
Views
Kotlin
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback() return true } return false
Java
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback(); return true; } return false;
Compose
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
o
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
Resultados
Tu app ahora puede responder cuando se presiona la barra espaciadora para pausar y reanudar un video o cualquier otro contenido multimedia.
Recursos adicionales
Para obtener más información sobre los eventos del teclado y cómo administrarlos, consulta Cómo controlar las entradas del teclado.
Rechazo de la palma con la pluma stylus
Una pluma stylus puede ser una herramienta excepcionalmente productiva y creativa en pantallas grandes. Sin embargo, cuando los usuarios dibujan, escriben o interactúan con una app usando una pluma stylus, a veces tocan la pantalla con la palma de las manos. El evento táctil se puede informar a tu app antes de que el sistema reconozca el evento y lo descarte como un toque de la palma accidental.
Prácticas recomendadas
Tu app debe identificar eventos táctiles extraños e ignorarlos. Para cancelar un toque de la palma, Android envía un objeto MotionEvent
. Busca ACTION_CANCEL
o ACTION_POINTER_UP
y FLAG_CANCELED
en el objeto para determinar si se rechazó el gesto causado por el toque de la palma.
Ingredientes
MotionEvent
: Representa eventos táctiles y de movimiento. Contiene la información necesaria para determinar si se debe ignorar un evento.OnTouchListener#onTouch()
: Recibe objetosMotionEvent
.MotionEvent#getActionMasked()
: Muestra la acción asociada con un evento de movimiento.ACTION_CANCEL
: Es una constanteMotionEvent
que indica que se debe deshacer un gesto.ACTION_POINTER_UP
: Es una constanteMotionEvent
que indica que un puntero distinto al primero se fue hacia arriba (es decir, renunció al contacto con la pantalla del dispositivo).FLAG_CANCELED
: Es una constanteMotionEvent
que indica que el puntero hacia arriba causó un evento táctil no intencional. Se agregó a los eventosACTION_POINTER_UP
yACTION_CANCEL
en Android 13 (nivel de API 33) y versiones posteriores.
Pasos
Resumen
Examina los objetos MotionEvent
que se enviaron a tu app. Usa las APIs de MotionEvent
para determinar las características del evento:
- Eventos de un solo puntero: Comprueba si se cumple el evento
ACTION_CANCEL
. En Android 13 y versiones posteriores, también comprueba si se cumple el eventoFLAG_CANCELED
. - Eventos de varios punteros: En Android 13 y versiones posteriores, comprueba si se cumplen los eventos
ACTION_POINTER_UP
yFLAG_CANCELED
.
Responde a los eventos ACTION_CANCEL
y ACTION_POINTER_UP
/FLAG_CANCELED
.
1. Adquiere objetos de eventos de movimiento
Agrega un objeto OnTouchListener
a tu app:
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> // Process motion event. } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { // Process motion event. });
2. Determina la acción y las marcas de los eventos
Busca ACTION_CANCEL
, que indica un evento de un solo puntero en todos los niveles de API. En Android 13 y versiones posteriores, busca el evento FLAG_CANCELED.
en ACTION_POINTER_UP
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> when (event.actionMasked) { MotionEvent.ACTION_CANCEL -> { //Process canceled single-pointer motion event for all SDK versions. } MotionEvent.ACTION_POINTER_UP -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } } true } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { switch (event.getActionMasked()) { case MotionEvent.ACTION_CANCEL: // Process canceled single-pointer motion event for all SDK versions. case MotionEvent.ACTION_UP: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } return true; });
3. Deshace el gesto
Una vez que hayas identificado el toque de la palma, puedes deshacer los efectos en pantalla del gesto.
Tu app debe mantener un historial de acciones del usuario para deshacer las entradas no deseadas, como los toques de la palma. Para ver un ejemplo, consulta Cómo implementar una app de dibujo básica en el codelab Cómo mejorar la compatibilidad con la pluma stylus en una app para Android.
Resultados
Ahora, tu app puede identificar y rechazar los toques de la palma de la mano para eventos de varios punteros en Android 13 y niveles posteriores de API, y para eventos de un solo puntero en todos los niveles de API.
Recursos adicionales
Para obtener más información, consulta lo siguiente:
- Funciones y APIsf de Android 13: Rechazo mejorado de la palma
- Guías para desarrolladores
- Codelab: Cómo mejorar la compatibilidad con la pluma stylus en una app para Android
Administración de estado de WebView
WebView
es un componente de uso general que ofrece un sistema avanzado para la administración de estados. Una WebView
debe mantener su estado y posición de desplazamiento en todos los cambios de configuración. Un objeto WebView
puede perder la posición de desplazamiento cuando el usuario rota el dispositivo o despliega un teléfono plegable, lo que lo obliga a desplazarse nuevamente desde la parte superior de la WebView
hasta la posición de desplazamiento anterior.
Prácticas recomendadas
Minimiza la cantidad de veces que se vuelve a crear un objeto WebView
. WebView
administra bien su estado y puedes aprovechar esta calidad administrando tantos cambios de configuración como sea posible. Tu app debe controlar los cambios de configuración porque la recreación de Activity
(la forma en que el sistema maneja los cambios de configuración) también recrea la WebView
, lo que hace que WebView
pierda su estado.
Ingredientes
android:configChanges
: Es el atributo del elemento<activity>
del manifiesto. Enumera los cambios de configuración que controla la actividad.View#invalidate()
: Es el método que vuelve a dibujar una vista. Lo heredaWebView
.
Pasos
Resumen
Para guardar el estado WebView
, evita la recreación de Activity
tanto como sea posible y, luego, deja que se invalide WebView
para que pueda cambiar de tamaño y retener su estado.
1. Agrega cambios de configuración al archivo AndroidManifest.xml
de tu app
Para evitar la recreación de actividades, especifica los cambios de configuración que maneja tu app (en lugar del sistema):
<activity
android:name=".MyActivity"
android:configChanges="screenLayout|orientation|screenSize
|keyboard|keyboardHidden|smallestScreenSize" />
2. Invalida WebView
cada vez que tu app recibe un cambio de configuración
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) webView.invalidate() }
Java
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); webview.invalidate(); }
Este paso se aplica solo al sistema de vista, ya que Jetpack Compose no necesita invalidar nada para cambiar el tamaño de los elementos Composable
correctamente. Sin embargo, Compose vuelve a crear una WebView
con frecuencia si no se administra correctamente. Usa el wrapper Accompanist WebView para guardar y restablecer el estado WebView
en tus apps de Compose.
Resultados
Los componentes WebView
de tu app ahora retienen su estado y posición de desplazamiento en varios cambios de configuración, desde el cambio de tamaño hasta el cambio de orientación, el plegado y el desplegado.
Recursos adicionales
Para obtener más información sobre los cambios de configuración y cómo administrarlos, consulta Cómo administrar los cambios en la configuración.
Administración del estado de RecyclerView
RecyclerView
puede mostrar grandes cantidades de datos usando recursos gráficos mínimos. A medida que una RecyclerView
se desplaza por su lista de elementos, la RecyclerView
reutiliza las instancias de View
de elementos que se desplazaron fuera de la pantalla para crear elementos nuevos a medida que se desplazan en la pantalla. Sin embargo, los cambios de configuración, como la rotación del dispositivo, pueden restablecer el estado de una RecyclerView
, lo que obliga a los usuarios a desplazarse nuevamente a su posición anterior en la lista de elementos RecyclerView
.
Prácticas recomendadas
RecyclerView
debe mantener su estado (en particular, la posición de desplazamiento) y el estado de los elementos de lista durante todos los cambios de configuración.
Ingredientes
RecyclerView.Adapter#setStateRestorationPolicy()
: Especifica cómo un objetoRecyclerView.Adapter
restablece su estado después de un cambio de configuración.ViewModel
: Contiene el estado de una actividad o un fragmento.
Pasos
Resumen
Establece la política de restablecimiento de estado del RecyclerView.Adapter
para guardar la posición de desplazamiento de RecyclerView
. Guarda el estado de los elementos de lista de RecyclerView
. Agrega el estado de los elementos de lista al adaptador RecyclerView
y restablécelo cuando estén vinculados a un ViewHolder
.
1. Habilita la política de restablecimiento de estado de Adapter
Habilita la política de restablecimiento de estado del adaptador RecyclerView
para que se mantenga la posición de desplazamiento de RecyclerView
en todos los cambios de configuración. Agrega la especificación de la política al constructor del adaptador:
Kotlin
class MyAdapter() : RecyclerView.Adapter() { init { stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY } ... }
Java
class MyAdapter extends RecyclerView.Adapter{ public Adapter() { setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY); } ... }
2. Guarda el estado de los elementos de lista con estado
Guarda el estado de los elementos complejos de la lista RecyclerView
, como elementos que contienen elementos EditText
. Por ejemplo, para guardar el estado de un EditText
, agrega una devolución de llamada similar a un controlador onClick
para capturar los cambios de texto. Dentro de la devolución de llamada, define qué datos se guardarán:
Kotlin
input.addTextChangedListener( afterTextChanged = { text -> text?.let { // Save state here. } } )
Java
input.addTextChangedListener(new TextWatcher() { ... @Override public void afterTextChanged(Editable s) { // Save state here. } });
Declara la devolución de llamada en tu Activity
o Fragment
. Usa un ViewModel
para almacenar el estado.
3. Agrega el estado del elemento de lista a Adapter
Agrega el estado de los elementos de lista a tu RecyclerView.Adapter
. Pasa el estado del elemento al constructor del adaptador cuando se cree el host Activity
o Fragment
:
Kotlin
val adapter = MyAdapter(items, viewModel.retrieveState())
Java
MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());
4. Recupera el estado del elemento de lista en el ViewHolder
del adaptador
En RecyclerView.Adapter
, cuando vinculas un ViewHolder
a un elemento, restablece el estado del elemento:
Kotlin
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { ... val item = items[position] val state = states.firstOrNull { it.item == item } if (state != null) { holder.restore(state) } }
Java
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ... Item item = items[position]; Arrays.stream(states).filter(state -> state.item == item) .findFirst() .ifPresent(state -> holder.restore(state)); }
Resultados
Tu RecyclerView
ahora puede restablecer su posición de desplazamiento y el estado de cada elemento de la lista RecyclerView
.
Recursos adicionales
Administración de teclado desmontable
La compatibilidad con teclados desmontables ayuda a maximizar la productividad del usuario en dispositivos grandes
dispositivos de pantalla. Android activa un cambio de configuración cada vez que se activa
se conecta o se desconecta de un dispositivo, lo que puede provocar una pérdida del estado de la IU. Tu
app puede guardar y restablecer su estado, lo que le permitirá al sistema controlar
recreación de actividades o restringe la recreación de actividades por cambios en la configuración del teclado.
En todos los casos, todos los datos relacionados con el teclado se almacenan en un
Objeto Configuration
. El keyboard
y
Los campos keyboardHidden
del objeto de configuración contienen información sobre el tipo
del teclado y su disponibilidad.
Prácticas recomendadas
Las apps optimizadas para pantallas grandes son compatibles con todo tipo de dispositivos de entrada, desde teclados de software y hardware a la pluma stylus, mouse, panel táctil y otros dispositivos periféricos dispositivos.
La compatibilidad con teclados externos implica cambios en la configuración, que puedes gestionar de dos maneras:
- Deja que el sistema vuelva a crear la actividad en ejecución y tú te encargas de administrar el estado de tu app.
- Administra el cambio de configuración por tu cuenta (no se recreará la actividad):
- Declara todos los valores de configuración relacionados con el teclado
- Crea un controlador de cambios de configuración
Apps de productividad, que a menudo requieren un control detallado de la IU para la entrada de texto y y otros aportes, pueden beneficiarse del enfoque hazlo tú mismo para manejar cambios de configuración.
En casos especiales, es posible que quieras cambiar el diseño de tu app cuando un hardware cuando el teclado está conectado o desconectado, por ejemplo, para liberar espacio para herramientas o editar ventanas.
Ya que la única forma confiable de escuchar los cambios de configuración es anular
método onConfigurationChanged()
de una vista, puedes agregar una vista nueva
a tu actividad en la app y responder en el archivo onConfigurationChanged()
de la vista
a los cambios de configuración causados por la conexión del teclado o
independiente.
Ingredientes
android:configChanges
: Es el atributo del elemento<activity>
del manifiesto de la app. Informa al sistema sobre los cambios de configuración que administra la app.View#onConfigurationChanged()
: Método que reacciona a la propagación de un nuevo elemento configuración de la app.
Pasos
Resumen
Declara el atributo configChanges
y agrega valores relacionados con el teclado. Agrega un
View
a la jerarquía de vistas de la actividad y escuchar para detectar cambios de configuración
1. Declara el atributo configChanges
Actualiza el elemento <activity>
en el manifiesto de la app. Para ello, agrega los valores keyboard|keyboardHidden
a la lista de cambios de configuración ya administrados:
<activity
…
android:configChanges="...|keyboard|keyboardHidden">
2. Cómo agregar una vista vacía a la jerarquía de vistas
Declara una nueva vista y agrega tu código de controlador dentro del método onConfigurationChanged()
de la vista:
Kotlin
val v = object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // Handler code here. } }
Java
View v = new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Handler code here. } };
Resultados
Ahora, tu app responderá cuando se conecte o desconecte un teclado externo sin volver a crear la actividad en ejecución.
Recursos adicionales
Para aprender a guardar el estado de la IU de tu app durante cambios de configuración, como la conexión o desconexión de un teclado, consulta Cómo guardar estados de la IU.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Cómo administrar los cambios en la configuración