Gestión de gestos: manejo de conflictos de gestos. Parte 3

Se preparó una traducción del artículo antes del lanzamiento de un curso avanzado y básico de desarrollo de Android.



Este es el tercer artículo de una serie sobre control de gestos. Puede familiarizarse con las traducciones de la primera y segunda parte si se las perdió.

En artículos anteriores, cubrimos el tema de llenar el espacio de la pantalla de borde a borde. En este artículo, veremos cómo manejar cualquier conflicto que surja entre los gestos de su aplicación y los nuevos gestos del sistema en Android 10 .

¿Qué queremos decir con conflictos de gestos? Veamos un ejemplo. Digamos que tenemos un reproductor de música que permite al usuario desplazarse por la canción actual arrastrando y soltando SeekBar.



Desafortunadamente, SeekBarestá demasiado cerca del área del gesto de volver a la pantalla de inicio, por lo que comienza el gesto de cambiar rápidamente a la aplicación anterior (QuickSwitch), lo que le da inconvenientes al usuario.

Lo mismo puede suceder en cualquier borde de la pantalla donde se encuentran las áreas de gestos. Hay muchos ejemplos comunes que pueden causar conflictos, como: cajones de navegación ( DrawerLayout), carruseles ( ViewPager ), controles deslizantes ( SeekBar), deslizar en las listas.

Lo que nos lleva a la pregunta "¿cómo podemos solucionar esto?" Para facilitar esta pregunta, hemos creado un diagrama de flujo que le ofrece una respuesta basada en la situación.


Puede encontrar una versión en PDF del diagrama de flujo aquí .

Espero que las preguntas no requieran explicación, pero por si acaso, examinaré cada una de ellas:

1. ¿La aplicación necesita ocultar las barras de navegación y estado?


La primera pregunta es si el caso de uso principal para su aplicación requiere ocultar las barras de navegación y / o estado. Al escondernos, queremos decir que estos paneles del sistema no son visibles en absoluto. Esto no significa que haya implementado el concepto de "borde a borde" o algo similar en su aplicación.

Posibles razones para responder "sí" a esta pregunta:


Ejemplos comunes de aplicaciones que deberían responder "sí" a esta pregunta son juegos, reproductores de video, visores de fotos, aplicaciones de dibujo.

2. ¿El escenario principal para usar la interfaz de usuario involucra deslizar dentro / alrededor del área de gestos?


Esta pregunta determina si su IU contiene algún elemento en / junto a las áreas de gestos (tanto "atrás" como "inicio") que el usuario debe deslizar.

Los juegos generalmente dicen que sí debido al hecho de que

  • Los controles en la pantalla generalmente se encuentran cerca del borde izquierdo / derecho y la parte inferior de la pantalla.
  • En algunos juegos, debe deslizar sobre elementos que pueden estar en cualquier lugar de la pantalla.

Además de los juegos, los ejemplos comunes de IU que deberían decir sí son:

  • UI recorta fotos donde los marcos arrastrables también están cerca de los bordes izquierdo y derecho de la pantalla.
  • Una aplicación de dibujo donde el usuario puede dibujar en un lienzo que cubre toda la pantalla.

3. ¿Vista frecuente en el área de gestos?


Espero que esta sea una pregunta bastante simple. Esto también incluye vistas, que cubren el área de gestos, y luego se extienden a una gran parte de la pantalla, por ejemplo, DrawerLayouto una gran ViewPager.

4. Ver implica deslizar / arrastrar?


Cambiamos un poco las tácticas y comenzamos a mirar puntos de vista individuales. Para cualquiera de los puntos de vista para los que respondió afirmativamente a la tercera pregunta, hacemos una pequeña aclaración: ¿el usuario debe deslizarla / arrastrarla?

Hay muchos ejemplos en los que tienen que responder "sí»: SeekBars, BottomSheet o incluso PopupMenu(hay que arrastrar para abierto).

5. ¿La vista está ubicada total / generalmente debajo de las áreas de gestos?


En base a la cuarta pregunta, ahora aclaramos si la vista se encuentra total o principalmente en el área de gestos.

Si su vista está en un contenedor desplazable como este RecyclerView, piense en esta pregunta de manera un poco diferente: ¿la vista expandida total / básicamente se encuentra debajo del área de gestos en todas las posiciones de desplazamiento ? Si el usuario puede desplazar la vista desde debajo del área de gestos, entonces no necesita hacer nada.

En el diagrama anterior, puede observar una pantalla completa de carrusel ( ViewPager) como ejemplo de una respuesta negativa y preguntarse por qué no es necesario manejar este caso. Esto se debe al hecho de que las áreas de gestos izquierda / derecha tienen un ancho relativamente pequeño (predeterminado: 20dpcada una) en comparación con el ancho de la vista. Típicoel ancho de la pantalla de su teléfono en orientación vertical es ~ 360dp, dejando ~ 320dp de espacio libre en el que el usuario no tiene dificultades (esto es casi el 90% de la pantalla). Incluso con márgenes internos / sangrías, el usuario aún puede desplazarse cómodamente por el carrusel.

6. ¿El borde de la vista se superpone a las áreas de gestos requeridas?


La última pregunta aclara si la vista se encuentra bajo alguna de las áreas de gestos requeridas. Si recuerda nuestro artículo anterior , recordará que las áreas obligatorias de los gestos del sistema son áreas de la pantalla donde los gestos del sistema siempre tienen prioridad.

En Android 10, solo hay un área de gestos obligatoria, que se encuentra en la parte inferior de la pantalla, que permite al usuario volver a casa o abrir sus últimas aplicaciones. Esto puede cambiar en futuras versiones de la plataforma, pero ahora solo necesitamos trabajar con la vista en la parte inferior de la pantalla.

Ejemplos típicos son:

  • Hoja de fondo sin modo , ya que tienden a colapsar en una pequeña vista de arrastrar y soltar en la parte inferior de la pantalla.
  • Un carrusel de desplazamiento horizontal en la parte inferior de la pantalla, por ejemplo, una interfaz con pegatinas.

Ahora que hemos resuelto las preguntas, esperamos que pueda encontrar una de las soluciones, así que veamos cada una de ellas con más detalle.

No hay conflictos que manejar


Comencemos con la "solución" más simple, ¡simplemente no haga nada !

Por supuesto, tal vez todavía haya espacio para optimizaciones que puede hacer (consulte la sección a continuación), pero afortunadamente no hay problemas serios al usar la aplicación con el modo de navegación por gestos activado.

Si el cronograma lo trajo aquí, pero aún siente que hay un problema, háganoslo saber . Tal vez nos perdimos algo.

Vista en movimiento desde áreas de gestos


Como aprendimos de nuestro artículo anterior , existen inserciones para decirle a su aplicación dónde están las zonas de gestos del sistema en la pantalla. Uno de los métodos que podemos usar para resolver conflictos de gestos es mover las vistas en conflicto de las áreas de gestos. Esto es especialmente importante para la vista en la parte inferior de la pantalla, ya que esta área es la zona de gestos obligatorios, y las aplicaciones no pueden usar las API de exclusión de gestos allí.

Echemos un vistazo a un ejemplo nuevamente. Tenemos una interfaz de reproductor de música que mostramos arriba. Contiene SeekBar, ubicado en la parte inferior de la pantalla, lo que permite al usuario desplazarse por la canción.


Reproductor de música UI con una barra de búsqueda en la parte inferior de la pantalla

Pero cuando un usuario intenta omitir una canción, esto sucede:


Grabar un gesto del sistema que está en conflicto con SeekBar

Esto se debe a que el área inferior del gesto se superpone a SeekBar, por lo que el gesto de regreso al hogar tiene prioridad. Aquí está la visualización de las zonas de gestos:



Una solución simple


La solución más simple aquí es agregar una sangría / margen adicional para que la barra de búsqueda se mueva hacia arriba desde el área de gestos. Algo como esto:



si arrastramos la barra de búsqueda en este ejemplo, verá que ya no activamos el gesto de regreso a casa: la


barra de búsqueda ya no entra en conflicto con el gesto del sistema inferior.

Para implementar esto, necesitamos usar las nuevas inserciones de gestos del sistema disponibles en API 29 y la biblioteca Jetpack Core v1.2.0 (actualmente en alfa ). En el ejemplo, aumentamos la sangría inferior SeekBarpara que coincida con el valor del recuadro de gesto inferior :

ViewCompat.setOnApplyWindowInsetsListener(seekBar) { view, insets ->
     //      view ,    system gesture insets
    view.updatePadding(
        bottom = insets.systemGestureInsets.bottom
    )
    insets
}

Si está interesado en aprender cómo facilitar el trabajo WindowInsets, puede leer nuestro otro artículo sobre este tema:

WindowInsets - Diseño de escuchas

Otras acciones


En este punto, puede decidir que el trabajo ya está hecho, y para algunos diseños, esta podría ser la solución final. Pero en nuestro ejemplo, la interfaz de usuario retrocedió visualmente con mucho espacio perdido debajo SeekBar. Por lo tanto, en lugar de simplemente empujar la vista hacia arriba, podemos rediseñar el diseño para evitar perder espacio: se


SeekBarmueve a la parte superior del panel de reproducción.

Aquí nos hemos trasladado SeekBara la parte superior del panel de reproducción, completamente fuera del área de gestos. Esto significa que ya no necesitamos aumentar artificialmente la altura del panel para acomodarlo SeekBar.

, . « ».

API


En nuestro artículo anterior, mencionamos que "las aplicaciones tienen la capacidad de excluir gestos del sistema para ciertas partes de la pantalla" . Las aplicaciones hacen esto usando las API de exclusión de gestos, que aparecieron por primera vez en Android 10. El

sistema proporciona dos funciones diferentes para excluir áreas de gestos: View.setSystemGestureExclusionRects()y Window.setSystemGestureExclusionRects(). Lo que debe usar depende de la aplicación: si usa Android View, el sistema prefiere la vista API; de lo contrario, use la WindowAPI.

La principal diferencia entre las dos API es que la API de Windowsespera que cualquier rectángulo esté en el espacio de coordenadas de la ventana. Si usa la vista, generalmente trabajará en el espacio de coordenadas de la vista. La API de vista se encarga de la transformación entre espacios de coordenadas, es decir, solo debe razonar en términos del contenido de la vista.

Veamos un ejemplo. Vamos a utilizar nuestro ejemplo de un reproductor de música nuevamente, que SeekBarse encuentra en todo el ancho de la pantalla. Solucionamos el conflicto SeekBarcon el gesto de volver a la pantalla de inicio en la sección anterior, pero todavía tenemos áreas de gestos izquierda y derecha que debemos cuidar.

Veamos qué sucede cuando un usuario intenta omitir una canción cuando el "control deslizante"SeekBar(arrastre circular) se encuentra cerca de uno de los bordes:


SeekBar tiene un conflicto con el área de gestos hacia atrás.

Dado que el control deslizante está debajo del área de gestos correcta, el sistema cree que el usuario está intentando regresar utilizando el gesto, por lo tanto, muestra la flecha hacia atrás. Esto es inconveniente para los usuarios, ya que probablemente no quisieron regresar en este momento. Podemos solucionar esto utilizando las API de exclusión de gestos mencionadas anteriormente para excluir los bordes del control deslizante.

Las API de excepción de gestos generalmente se llaman desde dos lugares: onDraw()cuando se visualiza su vista y de onLayout()otra manera. Tu vista pasaList<Rect>que contiene todos los rectángulos a excluir. Como se mencionó anteriormente, estos rectángulos deben estar en su propio sistema de coordenadas de vista.

Por lo general, crea una función como esta que se llamará desde onLayout()y / o onDraw():

private val gestureExclusionRects = mutableListOf<Rect>()
private fun updateGestureExclusion() {
   //   ,      Android 10+
   if (Build.VERSION.SDK_INT < 29) return
  // -,     
   gestureExclusionRects.clear()
      //   ,    .  SeekBar    .
   thumb?.also { t ->
       gestureExclusionRects += t.copyBounds()
   }
   //            view,       ,     
   // ,       
   systemGestureExclusionRects = gestureExclusionRects
}

Un ejemplo completo se puede encontrar aquí .

Después de agregar esto, rebobinar cerca de los bordes funciona como se esperaba:


SeekBar trabajando en el área de gestos hacia atrás.

Nota sobre el ejemplo anterior. SeekBarya lo hace automáticamente en Android 10, por lo que no hay necesidad de hacerlo usted mismo. Aquí lo hacemos solo como un ejemplo para mostrarle el esquema general.

Limitaciones


Aunque las API de exclusión de gestos pueden parecer la solución perfecta para resolver todos los conflictos de gestos, este no es el caso. Al usar las API de exclusión de gestos, declaras que el gesto de tu aplicación es más importante que las acciones de devolución del sistema. Esta es una declaración sólida, por lo que esta API está diseñada para ser una salida de emergencia cuando no puede hacer nada más.

Al usar las API de exclusión de gestos, declaras que el gesto de tu aplicación es más importante que la acción del sistema de retroceder.

Dado que el comportamiento que proporciona la API puede violar una experiencia cómoda para el usuario, el sistema limita su uso: las aplicaciones pueden excluir solo hasta 200dp por borde.

Aquí hay algunas preguntas comunes que los desarrolladores tienen cuando escuchan esto:

¿Por qué es necesaria la restricción? Espero que la explicación anterior ya te haya llevado a una razón. Creemos que es muy importante que el usuario pueda retroceder constantemente desde el deslizamiento lateral. Constantemente en todo el dispositivo, no una aplicación. Esta restricción puede parecer demasiado restrictiva, pero solo una aplicación, excluyendo todo el borde de la pantalla, es suficiente para causar molestias al usuario, lo que lleva a la eliminación de la aplicación o a algo más radical.

En otras palabras, el sistema de navegación siempre debe ser consistente y fácil de usar.

¿Por qué 200dp?El argumento para 200dp es bastante sencillo. Como mencionamos anteriormente, las API de exclusión de gestos están destinadas a ser utilizadas como último recurso, por lo que este límite se calculó como un múltiplo de varios objetivos táctiles importantes. El tamaño mínimo recomendado para un objetivo sensorial es 48dp.4 objetivos sensoriales × 48dp = 192dp. Agregue un poco más de sangría y obtenemos el valor 200dp.

¿Qué sucede si necesito excluir más de 200dp por borde? El sistema excluirá solo los 200dp más bajos que solicitó.


El sistema permite una solicitud para una altura total de 200 dp, contando desde el borde inferior de

Mi vista fuera de la pantalla, ¿se considera esto un límite?No, el sistema solo considera los rectángulos excluidos que están dentro de la pantalla. Del mismo modo, si la vista está parcialmente en la pantalla, solo se tiene en cuenta la parte visible del rectángulo solicitado.

Sumérgete en la próxima publicación


Quizás después de leer esto, se pregunte por qué no consideramos el lado derecho del diagrama de flujo. Las soluciones a continuación están diseñadas específicamente para aplicaciones que utilizan la pantalla completa en la representación. Hablaremos de ellos en la siguiente parte.






All Articles