Principio y uso de la animación de atributos de Android (Kotlin)

El artículo anterior escribió sobre Ver animación y animación de cuadros. . . . .

Entonces este artículo será una ola de animación de atributos. . . . .

Todos deberíamos saber que la animación de la Vista solo cambia la posición de dibujo de la Vista y no cambia las propiedades de la Vista, como las coordenadas de los cuatro vértices. Es decir, mueve una Vista desde su posición original A a una nueva posición B y permanece en B. Si hace clic en la posición B, no habrá respuesta al evento de clic. Por ejemplo, solo está disponible en el punto A. . . .

Entonces, si una animación tiene un movimiento de posición, pero también debe manejar el evento de clic, la animación Ver no puede satisfacer la demanda. . .

Pero la animación de la propiedad   puede compensar esta deficiencia. ¡Ver animación! ! ! ! ! !

 

La animación de atributo fue introducida por Android 3.0. Se puede decir que es una expansión de la animación de vista. Es una versión mejorada de la animación de vista, o puede reemplazar casi por completo la animación de vista, y la animación de atributo puede hacer cualquier objeto, no solo Ver. . . .

Debido a que solo se puede usar después de la 3.0, si desea usar la versión anterior a la 3.0, puede usar la biblioteca de código abierto: http://nineoldandroids.com/

La animación de propiedad es en realidad dos clases, ValueAnimator ObjectAnimator 

ValueAnimator  :

Establezca el valor inicial y el valor final, y hay un tiempo total. Obtenga el valor excesivo de cada nodo de tiempo durante este tiempo y use el valor para controlar el objetivo de la animación, de hecho, este tipo de escena no se usa mucho. . . . . . .

ObjectAnimator  :

Este se usa mucho, y básicamente se usa para controlar la animación. Puede utilizar directamente el objeto de tarea como objeto de animación. . . .

 

============ 【ValueAnimator 】 ============

Déjame hablar de  ValueAnimator

Uso: ValueAnimator.ofFloat ()

    /**
     *      ValueAnimator
     * */
    private fun valueAnimator() {
        // 从 0 过渡到 1
        val valueAnimator = ValueAnimator.ofFloat(0F, 1F)
        // 时间是 100毫秒
        valueAnimator.duration = 100
        // 监听进度
        valueAnimator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
            override fun onAnimationUpdate(animation: ValueAnimator?) {
                //获取动画进度值,通过这个值,去控制动画目标
                //比如 一个View背景颜色的百分比
                val animatedValue = animation?.animatedValue as Float
                Log.d(TAG, "进度:$animatedValue")
            }
        })
        valueAnimator.start()
    }

Mira el registro:

A partir de los resultados, podemos ver que este progreso de transición se ha retrasado muchas veces. . . . .

 

Veamos el código fuente de ValueAnimator.ofFloat ()

 

Puede pasar varios parámetros, por lo que:

Si ValueAnimator.ofFloat (0F, 1F) representa la transición de 0 a 1,

 

Si ValueAnimator.ofFloat (0F, 50F, 3F, 100F) representa la transición de 0 a 50, luego de 50 a 3, y luego de 3 a 100

En el código:

    /**
     *      ValueAnimator
     * */
    private fun valueAnimator() {
        // 从 0 过渡到 1
        val valueAnimator = ValueAnimator.ofFloat(0F, 50F,3F,100F)
        // 时间是 100毫秒
        valueAnimator.duration = 1000
        // 监听进度
        valueAnimator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
            override fun onAnimationUpdate(animation: ValueAnimator?) {
                //获取动画进度值,通过这个值,去控制动画目标
                //比如 一个View背景颜色的百分比
                val animatedValue = animation?.animatedValue as Float
                Log.d(TAG, "进度:$animatedValue")
            }
        })
        valueAnimator.start()
    }

Ver el resultado:

进度:0.0
进度:0.0
进度:0.09472668
进度:0.40268898
进度:0.9233728
进度:1.655303
进度:2.535273
进度:3.670764
进度:5.009651
进度:6.4521832
进度:8.17451
进度:10.087408
进度:12.056963
进度:14.323733
进度:16.763512
进度:19.211622
进度:21.966991
进度:24.87359
进度:27.73996
进度:30.916098
进度:34.217968
进度:37.43199
进度:40.950714
进度:44.566513
进度:48.049103
进度:48.285706          // 接近50时,开始从 50 往 3F去过度
进度:44.67541
进度:41.22999
进度:37.528618
进度:33.79583
进度:30.263401
进度:26.500002
进度:22.736603
进度:19.204176
进度:15.471386
进度:11.770042
进度:8.324593
进度:4.7142982           // 接近3时,开始从 3 往 100F去过度
进度:6.3578963
进度:13.540961
进度:20.555607
进度:27.381924
进度:33.617188
进度:40.022766
进度:46.184475
进度:51.745235
进度:57.384045
进度:62.423447
进度:67.47879
进度:72.21197
进度:76.36032
进度:80.43042
进度:84.14144
进度:87.29662
进度:90.28128
进度:92.878716
进度:94.96303
进度:96.78872
进度:98.20865
进度:99.17078
进度:99.79256
进度:100.0

Además de ValueAnimator.ofFloat, los más utilizados son: ValueAnimator.ofInt ()

El principio es el mismo. No los mostraré uno por uno. . . . . .

 

Además, también podemos llamar

método setStartDelay () para establecer el tiempo de retardo de la animación,

setRepeatCount ()  establece el número de veces que la animación se reproduce en un bucle

setRepeatMode () es el modo de reproducción de bucle. Los modos de bucle incluyen RESTART y REVERSE, que significan respectivamente repetición y reproducción inversa.

Estos métodos son muy simples y no se explicarán en detalle.
 

 

============ 【ObjectAnimator 】 ==========

ObjectAnimator  es diferente: puede animar cualquier objeto, como el color de fondo de la vista y el valor alfa.

Del código fuente, ObjectAnimator   hereda  ValueAnimator,

Entonces, en el análisis final, la   animación se opera de la manera en que ValueAnimator establece el valor. Entonces ValueAnimator es el núcleo de toda la animación de propiedades.

Entonces    , el método ValueAnimator , en  ObjectAnimator   casi todo el uso de. . . . Entonces  ObjectAnimator   puede reemplazar casi por completo a ValueAnimator   . . . .

De hecho, el uso de ObjectAnimator    es similar, mira el código ----

 

Miramos la transparencia (alfa)

private fun objectAnimator() {
        /**
         *      参数1     tv_animator_view 目标View
         *      参数2     需要进行动画的属性 比如:alpha ,这个参数字符乱传会怎样? 后面会说到
         *      参数3...  这里可以传进多个参数,现在代码设置就是:0F, 1F, 0F, 1F
         *                就是从 0(完全透明)过渡到 1 (完全不透明) 再到 0(完全透明) 再到 1 (完全不透明)
         * */
        val objectAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        objectAnimator.duration = 4000
        objectAnimator.start()
    }

OK, ejecute el código, tv_animator_view se muestra como se mencionó anteriormente:

Transición de completamente transparente a completamente opaco, luego completamente transparente y luego completamente opaco

aprender por analogía,

 

Miramos rotación (rotación)

val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view2, "rotation", 0F, -90F,90F,0F)
        rotationAnimator.duration = 10000
        rotationAnimator.start()

Recuerda lo que dije en el artículo anterior, la configuración de rotación

Los números positivos representan el método de las agujas del reloj,

Los números negativos representan el enfoque en sentido antihorario.

Entonces, el efecto de la operación anterior es: primero gire desde la posición original a 90 grados en sentido antihorario, luego gire desde la posición actual a 90 grados en sentido horario y luego vuelva a la posición original.

No publicaré las representaciones de la animación en tiempo de ejecución. . . . problema. . .

 

Veamos la traducción (translationX o translationY) :

        //获取当前 View X方向偏量 如果在原始位置,XY轴的偏移量都是0
        val originX = tv_animator_view3.translationX
        //获取 View 的宽度,返回值单位 px像素
        val width = tv_animator_view3.width
        /**
         *          参数1     tv_animator_view3 目标View
         *          参数2     translationX X轴的平移 正为往右  负数为左,单位为像素
         *          参数3     这里可以传进多个参数,
         *                    运行结果:从原始位置 往左移动 当前View的宽度,然后再回到原始位置
         * */
        val translationAnimator =
            ObjectAnimator.ofFloat(tv_animator_view3, "translationX", originX, width.toFloat(), originX)
        translationAnimator.duration = 10000
        translationAnimator.start()

El resultado de la operación es lo que escribí en el comentario: mueva el ancho de la Vista actual desde la posición original hacia la izquierda y luego regrese a la posición original

Tenga en cuenta las unidades en el método:

traslación X La traslación del eje X es positiva a la derecha, negativa a la izquierda, la unidad es el píxel

traslación Y La traslación del eje Y es hacia abajo, la negativa es hacia arriba, la unidad es el píxel

 

Veamos escalado (scaleX o scaleY) :

        val translationAnimator =
            ObjectAnimator.ofFloat(tv_animator_view3, "scaleX", 0F, 1F)
        translationAnimator.duration = 10000
        translationAnimator.start()

Al mirar el código, puede ver que los inteligentes están haciendo zoom de 0 a 1 (tamaño original) en la dirección del eje X.

Si cambia a  scaleY  , hará zoom en la dirección vertical. . . . .

 

Hasta aquí. Se muestran las cuatro animaciones más utilizadas (alfa, rotación, traducciónX y escalaY) de animación de atributos.

Pero al igual que la pregunta de la parte superior, el segundo parámetro del método, qué más se puede completar, responderé por usted: cualquier carácter.

que ??? estas bromeando? No, no estoy bromeando.

Al diseñar Android, no hay límite solo para Ver, que puede ser cualquier subclase de Objeto.

Por lo tanto, si pasa abd en ObjectAnimator.ofFloat (tv_animator_view3, " abd ", 0F, 1F), no habrá efecto de animación en esta Vista.

Por supuesto, no va a ser carsh ...

Puedes entenderlo de   esta manera. Después de pasar un "scaleX", la animación continuará buscando el objetivo mientras la animación está en progreso. ¿El objetivo que se pasa actualmente tiene el método setScaleX () ?

¡Toca la pizarra! ¡Nota! ! Es el método correspondiente 

Si lo hay, llamará a este método de acuerdo con el progreso. De lo contrario, no se procesará.

Y lo que se pasa al principio es TextView, que tiene un método setScaleX () , y la animación lo cambiará.

Pero si lo que paso es " abd ", TextView no tiene el método setAbd (), por lo que no cambiará nada. . . . No hay error

El método setScaleX () de TextView estáen realidad en su clase principal, View. El código fuente es el siguiente:

 

Entonces llama a este setScaleX () of  View

Por analogía, ustedes que son inteligentes también pensarán que en la Vista definitivamente habrá:

Los métodos setRotation (), getRotation (), setTranslationX (), getTranslationX (), setScaleY (), getScaleY (). . . . .

 

En cuanto a cómo el mecanismo ObjectAnimator encuentra el método set () correspondiente. . . De hecho, este es el caso, ObjectAnimator depende del hilo actual Looper, si el hilo actual no tiene Looper, se informará un error. Desde el seguimiento de ObjectAnimator.start () hasta el método final ValueAnimator.star () se puede ver.

En cuanto a cómo crear el Looper actual, no es el contenido de este capítulo. Los lectores van solos a la matrícula. . . . .

De hecho, el código fuente se ha seguido hasta el final animateValue (fracción flotante),

Hay un fragmento de código llamado: mValues ​​[i] .calculateValue (fracción);  este código se usa para calcular el valor de atributo de cada marco

 

Bien, echemos un vistazo a cómo la animación de propiedades obtiene los métodos get () y set (). Esto es lo que más nos importa. . . .

El método get () se obtiene cuando no estamos inicializados. En  PropertyValuesHolder

Obtenido por reflexión

 

Entonces, ¿qué pasa con set ()  ? De hecho, también está en  PropertyValuesHolder  . Debido a que creamos el segundo parámetro pasado por ObjectAnimator,

Se almacena en esta clase, por lo que  get y set se obtienen a través de  PropertyValuesHolder  .

El seguimiento del código fuente anterior es solo una interceptación de la parte clave, el lector encontrará la ruta de seguimiento específica por sí mismo, jaja

 

 

============ [ Agencia intermedia de animación ] ==================

Bien, hagamos una demostración para verificar el método de animación de propiedades  : propiedad

 

Primero creamos una clase AnimationTemp

class AnimationTemp {

    private val TAG = "AnimationTemp"


    fun setAbcint(abcInt: Int) {
        Log.d(TAG, "调用 setAbcInt() abcInt :  $abcInt ")
    }

    fun getAbcint(): Int {
        Log.d(TAG, "调用 getAbcInt() ")
        return 100
    }

}

 

Luego, de acuerdo con el principio de la animación de atributos, el objeto de animación puede ser cualquier objeto, bueno. Usamos este AnimationTemp como objeto de animación.

        /**
         *          参数1   以 AnimationTemp 作为 动画对象
         *          参数2   改变 abcint 这个属性,
         *                  对应会在目标对象里找:setAbcint(abcInt: Int) 这个方法,注意,方法名大小写
         *          参数3   起始值
         *          参数4   结束值
         * */
        val ofInt = ObjectAnimator.ofInt(AnimationTemp(), "abcint", 0, 10)
        ofInt.duration = 100
        ofInt.start()

 

Mira los resultados de ejecución:

De acuerdo, es obvio. . . . . Siga llamando a  setAbcint (abcInt: Int)   para imprimir de 0 a 10

 

En resumen, analice el principio:

1> ObjectAnimator.onInt () crea una animación con un degradado Int. El objeto de animación es una instancia de AnimationTemp. La propiedad que debe cambiarse se llama: abcint

2> Cuando la animación está en progreso, el sistema intenta cambiar el abcint del  objeto de animación  , luego el correspondiente

        El sistema buscará un método llamado: setAbcint (abcInt: Int) en el objeto de animación , preste atención al tipo de parámetros de entrada  del método y el nombre en mayúsculas y minúsculas del nombre del método

3> Entonces, durante todo el ciclo de animación, llame a  setAbcint (abcInt: Int)   para cambiar el valor de propiedad de la animación de destino

 

En este momento, puede ver que nuestro getAbcint () no está llamado. De hecho, el código fuente anterior ha sido analizado, es decir, será llamado cuando no tengamos un valor de inicialización.

Bien, cambiemos el código:

 Al crear una animación, pase el valor final (si solo pasa un Int, el sistema utiliza este valor como valor final de forma predeterminada)

        /**
         *          参数1   以 AnimationTemp 作为 动画对象
         *          参数2   改变 abcint 这个属性,
         *                  对应会在目标对象里找:setAbcint(abcInt: Int) 这个方法,注意,方法名大小写
         *          参数3   结束值
         * */
        val ofInt = ObjectAnimator.ofInt(AnimationTemp(), "abcint", 10)
        ofInt.duration = 100
        ofInt.start()

Entonces AnimationTemp permanece sin cambios

class AnimationTemp {

    private val TAG = "AnimationTemp"


    fun setAbcint(abcInt: Int) {
        Log.d(TAG, "调用 setAbcInt() abcInt :  $abcInt ")
    }

    fun getAbcint(): Int {
        Log.d(TAG, "调用 getAbcInt() ")
        return 100
    }
}

 

Mira los resultados:

De acuerdo, es obvio. Verifique el análisis del código fuente.

Cuando el valor inicial de la animación no se inicializa, el método get () de la propiedad correspondiente se encontrará en el destino de la animación como valor inicial.

Aquí devolvemos 100 como valor inicial, y luego el valor final es 10

Así que imprime de 100 a 10

Entonces, en este punto, todos conocen básicamente el principio de la animación de atributos. ¡Todos comprenden por qué el objetivo de la animación de atributos puede ser cualquier objetivo! ! ! !

============ [ Animación combinada ] ==================

Termina de hablar del principio. Por último, hablemos de varias animaciones juntas. . Animación combinada. . .

AnimatorSet    usa esta clase. Puede poner en juego uno o más ValueAnimator y ObjectAnimator

Método principal:

play ()                            reproducir un sencillo

playSequentially () se        reproduce secuencialmente, uno tras otro

playTogether ()              reproduce todas las animaciones juntas

 

después de (Animator animIn)             inserte la animación existente en la animación entrante (es decir: animIn ), ejecute
después (retraso largo)                      para retrasar  la animación existente durante milisegundos especificados y ejecute
antes (Animator animIn)         para insertar la animación existente en la animación entrante (Es decir: animIn ) La ejecución previa de
with (Animator anim)  ejecutará               la animación existente y la animación pasada (es decir: animIn ) al mismo tiempo

Demo1 ---- después de

Bien, ejecutemos el código:

        val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        alphaAnimator.duration = 4000

        val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
        rotationAnimator.duration = 4000

        val animatorSet = AnimatorSet()
        animatorSet.play(alphaAnimator).after(rotationAnimator)
        animatorSet.start()

 

El efecto de ejecución es: RotationAnimator se ejecuta primero, alphaAnimator se ejecuta

 【Demo2 ---- con

        val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        alphaAnimator.duration = 4000

        val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
        rotationAnimator.duration = 4000

        val animatorSet = AnimatorSet()
        animatorSet.play(alphaAnimator).with(rotationAnimator)
        animatorSet.start()

 

El efecto de ejecución es: RotationAnimator y alphaAnimator se ejecutan al mismo tiempo

 

 【Demo3 ---- antes

        val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        alphaAnimator.duration = 4000

        val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
        rotationAnimator.duration = 4000

        val animatorSet = AnimatorSet()
        animatorSet.play(alphaAnimator).before(rotationAnimator)
        animatorSet.start()

El efecto de ejecución es: alphaAnimator se ejecuta primero y RotationAnimator se ejecuta más tarde

 

Demo4 - después (largo)

        val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        alphaAnimator.duration = 4000

        val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
        rotationAnimator.duration = 4000

        val animatorSet = AnimatorSet()
        animatorSet.play(alphaAnimator).before(rotationAnimator).after(3000)
        animatorSet.start()

El efecto de ejecución es: demora 3 segundos, alphaAnimator se ejecuta primero, rotaciónAnimator se ejecuta más tarde

 

Demo5 ----  jugar juntos

        val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        alphaAnimator.duration = 4000

        val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
        rotationAnimator.duration = 4000

        val animatorSet = AnimatorSet()
        animatorSet.playTogether(alphaAnimator, rotationAnimator)
        animatorSet.start()

El efecto de ejecución es: RotationAnimator y alphaAnimator se ejecutan al mismo tiempo

 

Demo5 ----  reproducir secuencialmente

        val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        alphaAnimator.duration = 4000

        val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
        rotationAnimator.duration = 4000

        val animatorSet = AnimatorSet()
        animatorSet.playSequentially(alphaAnimator, rotationAnimator)
        animatorSet.start()

El efecto de ejecución es: alphaAnimator se ejecuta primero y RotationAnimator se ejecuta más tarde

 

================== [ Monitorización de animación ] =================

Es en realidad un método

        val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
        alphaAnimator.duration = 4000

        val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
        rotationAnimator.duration = 4000

        val animatorSet = AnimatorSet()
        animatorSet.addListener(object : Animator.AnimatorListener{
            override fun onAnimationRepeat(animation: Animator?) {
                //重复
            }

            override fun onAnimationEnd(animation: Animator?) {
                //结束
            }

            override fun onAnimationCancel(animation: Animator?) {
                //取消
            }

            override fun onAnimationStart(animation: Animator?) {
                //开始
            }

        })
        animatorSet.playSequentially(alphaAnimator, rotationAnimator)
        animatorSet.start()

 

De hecho, no sabe si está seguro, ¿ha descubierto que la animación de atributo no ha encontrado setFillAfter () para establecer si la animación se restaura al estado inicial?

Bueno, podemos monitorear el final de la animación y luego establecer el estado de destino cuando termina

public class ResetAnimatorListenerAdapter extends AnimatorListenerAdapter {

    View animatorView;

    public ResetAnimatorListenerAdapter(View animatorView) {
        this.animatorView = animatorView;
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        //重置 animatorView 的状态
        animatorView.setTranslationY(0);
        animatorView.setAlpha(0.0f);
    }

    @Override
    public void onAnimationCancel(Animator animation) {
        super.onAnimationCancel(animation);
        
    }
    
}

 

 

Lo mencionado anteriormente, el código de Chase establece dinámicamente la animación, de hecho, también se puede configurar a través de xml, sin mencionar los detalles. Debido a que no se recomienda usar xml, está relativamente muerto y no se puede cambiar.

 

Finalmente, las advertencias de la animación.

1: Evite OOM y evite el uso de la animación de cuadros con más imágenes. . .

2: para evitar pérdidas de memoria, detenga la animación cuando la vista salga invisible

3: La animación de atributos se utiliza por encima de Android3.0, inferior a esta versión, puede utilizar la biblioteca compatible

4: Ver animación, cuando la vista se mueve, la configuración de View.GONE a veces se vuelve inválida. En este momento, simplemente llame a View.clearAnimation () para borrar la animación.

5: Cuando la animación está en progreso, puede activar la aceleración de hardware para mejorar la fluidez.

 

No hay ningún problema con el código anterior, deje un mensaje para corregir cualquier problema, gracias

Supongo que te gusta

Origin blog.csdn.net/Leo_Liang_jie/article/details/90812632
Recomendado
Clasificación