Prepárate para 12L, una actualización de funciones para pantallas grandes que se lanzará a principios del próximo año. Pruébala hoy.

Efecto de sobredesplazamiento

En dispositivos que ejecutan Android 12 y versiones posteriores, cambia el comportamiento visual de los eventos de sobredesplazamiento.

En Android 11 y versiones anteriores, un evento de sobredesplazamiento causa que los elementos visuales brillen. En Android 12 y versiones posteriores, los elementos visuales se estiran y rebotan en un evento de arrastre, y se deslizan y rebotan en un evento de deslizamiento:

Los comportamientos nuevos de sobredesplazamiento afectan las animaciones de arrastre y deslizamiento.

El comportamiento se aplica a todas las apps que usan EdgeEffect y a todo el contenido dentro de las siguientes clases:

El efecto visual funciona para el desplazamiento vertical y horizontal. Como se aplica de forma predeterminada a todas las apps que no inhabilitan el sobredesplazamiento, ofrece una experiencia de IU más coherente para los usuarios.

Recomendaciones

Para asegurarte de que la experiencia nueva de sobredesplazamiento funcione bien en la app, sigue estas prácticas recomendadas:

  • Modifica de manera correcta el tamaño de los contenedores de desplazamiento de modo que sus vistas secundarias se ajusten a sus límites.
  • Usa valores positivos para deltaDistance en EdgeEffect.onPull(deltaDistance, displacement) cuando se agregue al efecto de estiramiento del sobredesplazamiento y valores negativos cuando se reduzca este efecto.
  • Captura la animación cuando deslices hacia abajo.

Usa EdgeEffect para el efecto de estiramiento

EdgeEffect agrega dos API para implementar el efecto de sobredesplazamiento de estiramiento.

float getDistance()
float onPullDistance(float deltaDistance, float displacement)

Para ofrecer la mejor experiencia del usuario con este efecto, haz lo siguiente:

  • Cuando el usuario suelte y toque contenido durante la animación de la acción soltar, registra el toque como una "captura". El usuario detiene la animación y comienza a manipular el efecto de estiramiento otra vez.
  • Cuando el usuario mueva el dedo en la dirección opuesta al efecto, libéralo hasta que desaparezca por completo; luego, comienza el desplazamiento.
  • Cuando el usuario desliza el dedo durante un efecto de estiramiento, desplaza EdgeEffect para mejorar este efecto.

Captura la animación

Cuando un usuario captura una animación activa del efecto de estiramiento, EdgeEffect.isFinished() muestra false. Es decir, se indica que este efecto debe manipularse mediante movimientos táctiles. En la mayoría de los contenedores, se detecta en onInterceptTouchEvent(), como se muestra en el siguiente fragmento de código:

Kotlin

override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
  ...
  when (action and MotionEvent.ACTION_MASK) {
    MotionEvent.ACTION_DOWN ->
      ...
      isBeingDragged = !edgeEffectBottom.isFinished() ||
          !edgeEffectTop.isFinished()
      ...
  }
  return isBeingDragged
}

Java

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
  ...
  switch (action & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
      ...
      mIsBeingDragged = !mEdgeEffectBottom.isFinished()
          || !mEdgeEffectTop.isFinished();
      ...

En el ejemplo anterior, onInterceptTouchEvent() muestra true cuando mIsBeingDragged es true, por lo que es suficiente para consumir el evento antes de que el elemento secundario tenga la oportunidad de hacerlo.

Libera el efecto de sobredesplazamiento

Es importante liberar el efecto de estiramiento antes del desplazamiento para evitar que este efecto se aplique al contenido que se desplaza. En la siguiente muestra de código, se puede observar lo siguiente:

Kotlin

override fun onTouchEvent(ev: MotionEvent): Boolean {
  val activePointerIndex = ev.actionIndex

  when (ev.getActionMasked()) {
    MotionEvent.ACTION_MOVE ->
      val x = ev.getX(activePointerIndex)
      val y = ev.getY(activePointerIndex)
      var deltaY = y - mLastMotionY
      val pullDistance = deltaY / height
      val displacement = x / width

      if (deltaY < 0f && mEdgeEffectTop.distance > 0f) {
        deltaY -= height * mEdgeEffectTop
            .onPullDistance(pullDistance, displacement);
      }
      if (deltaY > 0f && mEdgeEffectBottom.distance > 0f) {
        deltaY += height * mEdgeEffectBottom
            .onPullDistance(-pullDistance, 1 - displacement);
      }

      ...
  }

Java

@Override
public boolean onTouchEvent(MotionEvent ev) {

  final int actionMasked = ev.getActionMasked();

  switch (actionMasked) {
    case MotionEvent.ACTION_MOVE:
      final float x = ev.getX(activePointerIndex);
      final float y = ev.getY(activePointerIndex);
      float deltaY = y - mLastMotionY;
      float pullDistance = deltaY / getHeight();
      float displacement = x / getWidth();

      if (deltaY < 0 && mEdgeEffectTop.getDistance() > 0) {
        deltaY -= getHeight() * mEdgeEffectTop
            .onPullDistance(pullDistance, displacement);
      }
      if (deltaY > 0 && mEdgeEffectBottom.getDistance() > 0) {
        deltaY += getHeight() * mEdgeEffectBottom
            .onPullDistance(-pullDistance, 1 - displacement);
      }
            ...

En un evento de arrastre, antes de pasar el evento táctil al desplazamiento anidado o antes de arrastrar el desplazamiento, se debe consumir la distancia de la acción tirar de EdgeEffect. En la muestra de código anterior, getDistance() muestra un valor positivo cuando se observa un efecto en el límite que se puede liberar con movimiento. Cuando el evento táctil libera el efecto de estiramiento, primero EdgeEffect lo consume, de modo que se liberará por completo antes de que se muestren otros efectos, como el desplazamiento anidado. Puedes usar getDistance() a fin de saber la distancia de la acción tirar que se necesita para liberar el efecto actual.

onPullDistance() es diferente a onPull(), ya que muestra la cantidad consumida del delta que se pasó. Anteriormente, onPull() permitía valores negativos para la distancia total de los efectos de brillo. En Android 12 y versiones posteriores, si a onPull() o onPullDistance() se les pasan valores deltaDistance negativos cuando getDistance() es 0, no se producirán cambios en el efecto de estiramiento.

No participar

Puedes inhabilitar el sobredesplazamiento en el archivo de diseño XML o de manera programática. En el siguiente código XML, se muestra android:overScrollMode configurado en el archivo de diseño.

<!-- Via markup -->
<ScrollView
  ...
  android:overScrollMode="never"
  ...
>

Inhabilita el sobredesplazamiento de forma programática, como se muestra en el siguiente fragmento de código:

Kotlin

<!-- Programmatically-->
...
recyclerview.overScrollMode = View.OVER_SCROLL_NEVER
...

Java

<!-- Programmatically-->
...
recyclerview.setOverScrollMode(View.OVER_SCROLL_NEVER);
...

Envía comentarios

Tus comentarios son importantes para nosotros. Avísanos si encuentras problemas o tienes ideas para mejorar esta función. Consulta los errores existentes antes de crear uno nuevo. Para agregar tu voto a un error existente, haz clic en el botón de la estrella.

Crear un error nuevo

Consulta la documentación sobre la Herramienta de seguimiento de errores para obtener más información.