[Android] La historia que Animation y Gone tienen que contar: analiza el impacto de la animación en View

fondo

View's Animation y Gone ya son muy familiares para todos. La animación es responsable de agregar animación a la Vista y Gone puede ocultar la Vista.

Entonces, cuando la animación de una Vista no finaliza, ¿establecer Gone finalizará la animación? ¿Se ocultará la vista?

Este es un fenómeno que encontré durante el proceso de desarrollo, simplemente restaure la escena:

Primero personalice LoadingView, la implementación es muy simple. Después de configurar el fondo, cuando esté visible, inicie una animación que gire sobre sí misma:

public class LoadingView extends View {
    
    

    private RotateAnimation animation;

    private void init(){
    
    
        animation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f);
        animation.setRepeatMode(Animation.RESTART);
        animation.setInterpolator(new LinearInterpolator());
        animation.setRepeatCount(-1);
        animation.setDuration(1250);
	    setBackgroundResource(R.drawable.loading_frame);
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
    
    
        super.onVisibilityChanged(changedView, visibility);
        if(visibility == View.VISIBLE){
    
    
            if (!animation.hasStarted()||animation.hasEnded()) {
    
    
                startAnimation(animation);
            }
        }
    }
}

Luego use LoadingView en el archivo de diseño y use FrameLayout como la capa más externa:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
            <LoadingView
            android:id="@+id/progress"
            android:layout_width="42dp"
            android:layout_height="42dp"
            android:layout_gravity="center"
            />
    
</FrameLayout>

Finalmente llame a Actividad activada:

mProgressView.setVisibility(GONE)

Como resultado, la carga sigue dando vueltas por ahí.
inserte la descripción de la imagen aquí

Verifique las propiedades de LoadingView, la visibilidad desapareció:

inserte la descripción de la imagen aquí

El ancho y el alto también tienen valores.

inserte la descripción de la imagen aquí

Parece ser un problema, pero se esconden al menos dos detalles detrás:

1. Ver animación y dibujo
2. Procesamiento finalizado en el diseño principal

Ver animación y dibujo.

En los comentarios oficiales de View, el dibujo se divide en los siguientes pasos:
inserte la descripción de la imagen aquí

  1. dibujar fondo
  2. Guarde las capas del lienzo como preparación para los degradados, si es necesario
  3. dibujar el contenido de la vista
  4. dibujar subvistas
  5. Dibuje bordes degradados y restaure capas si es necesario
  6. Dibujar decoraciones (por ejemplo, barras de desplazamiento)

El cuarto paso es dibujar la subvista. Existe un fragmento de código en ViewGroup:

protected void dispatchDraw(Canvas canvas) {
    
    
    for (int i = 0; i < childrenCount; i++) {
    
    
         ...
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
    
    
            more |= drawChild(canvas, child, drawingTime);
        }
    }
}

El código muestra que si la subvista es visible o si hay animación, se llamará a la lógica de dibujo de la subvista .

Por lo tanto, bajo la premisa de la animación, es imposible ocultar la Vista usando gone .

Manejo de desaparecidos en el diseño principal

La vista está oculta, generalmente pensamos que el ancho y el alto de esta Vista son ambos 0.

De hecho, durante el proceso de medición del diseño, se omite la subvista cuya propiedad de visibilidad ha desaparecido.

# FrameLayout

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    

   for (int i = 0; i < count; i++) {
    
    
      final View child = getChildAt(i);
      if (mMeasureAllChildren || child.getVisibility() != GONE) {
    
    
          if (measureMatchParentChildren) {
    
    
              if (lp.width == LayoutParams.MATCH_PARENT ||
                      lp.height == LayoutParams.MATCH_PARENT) {
    
    
                  mMatchParentChildren.add(child);
              }
          }
      }
  }

}
# LinearLayout

void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
    
    
	...
	for (int i = 0; i < count; ++i) {
    
    
		...
        if (child.getVisibility() == GONE) {
    
    
            i += getChildrenSkipCount(child, i);
            continue;
        }
     	...
	 }

}

Por lo tanto, las subvistas cuya propiedad desapareció no se han vuelto a medir y sus tamaños siguen siendo los últimos medidos.

evento táctil

Una vez identificados los problemas anteriores, surgió otra pregunta:

La vista secundaria que está configurada para realizar una animación al mismo tiempo es visible, entonces, ¿puede recibir eventos táctiles?

Después de reproducir el registro, se descubre que es posible.

La razón de esto se puede encontrar en el código fuente.

El evento Touch de la Vista secundaria lo pasa el control principal. En el método de distribución de eventos de ViewGroup, existe el siguiente código:

#ViewGroup
    
public boolean dispatchTouchEvent(MotionEvent ev) {
    
    
	...
    if (!child.canReceivePointerEvents()
           || !isTransformedTouchPointInView(x, y, child, null)) {
    
    
       ev.setTargetAccessibilityFocus(false);
       continue;
   }
	...
}

Si child.canReceivePointerEvents()se devuelve false, se omite la distribución.

canReceivePointerEventsEl código es el siguiente:

#View

protected boolean canReceivePointerEvents() {
    
    
    return (mViewFlags & VISIBILITY_MASK) == VISIBLE || getAnimation() != null;
}

Aunque está configurado "Desaparecido", se puede recibir el evento Touch porque la animación no está vacía.

otro

Incluso si la Vista está configurada para que desaparezca al principio y el usuario no puede verla, la Vista continuará realizando animaciones y consumiendo el rendimiento del sistema.

Así que tenga cuidado al utilizar animaciones y cancele clearAnimationlas animaciones a tiempo.

Resumir

  1. clearAnimationCuando encontré este problema al principio, lo resolvió llamándolo . Pero no puedo entender el motivo y no podré hacerlo la próxima vez que me encuentre con un problema similar. Para comprender el principio, el problema que se puede resolver en un minuto tomó menos de un día y personalmente creo que vale la pena.
  2. Domine el conocimiento de la distribución de eventos de antemano y sea objetivo al analizar los eventos táctiles.
  3. Familiarizado con Choreographer y el proceso de actualización de la interfaz, podrá comprender rápidamente el proceso de ejecución de la animación.
  4. La atención se centra en la acumulación, algunos conocimientos pueden no ser útiles en este momento, pero sabrás lo cómodo que es cuando los uses.

Materiales de referencia:
cómo se actualiza la interfaz
Ver el principio de funcionamiento de la animación

Supongo que te gusta

Origin blog.csdn.net/qq_23049111/article/details/127531493
Recomendado
Clasificación