Cambio animado del color de fondo de la vista en Android

335

¿Cómo se anima el cambio de color de fondo de una vista en Android?

Por ejemplo:

Tengo una vista con un color de fondo rojo. El color de fondo de la vista cambia a azul. ¿Cómo puedo hacer una transición suave entre colores?

Si esto no se puede hacer con vistas, una alternativa será bienvenida.

hpique
fuente
11
Uno esperaría que una transición suave entre colores no sea un problema para una plataforma como Android. Sin embargo, tal vez las vistas no están construidas para ello. La pregunta sigue en pie.
hpique
1
Un buen video tutorial
Alex Jolig

Respuestas:

374

¡Terminé buscando una solución (bastante buena) para este problema!

Puede usar un TransitionDrawable para lograr esto. Por ejemplo, en un archivo XML en la carpeta dibujable, podría escribir algo como:

<?xml version="1.0" encoding="UTF-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The drawables used here can be solid colors, gradients, shapes, images, etc. -->
    <item android:drawable="@drawable/original_state" />
    <item android:drawable="@drawable/new_state" />
</transition>

Luego, en su XML para la Vista real, haría referencia a este TransitionDrawable en el android:backgroundatributo.

En este punto, puede iniciar la transición en su código de comando haciendo lo siguiente:

TransitionDrawable transition = (TransitionDrawable) viewObj.getBackground();
transition.startTransition(transitionTime);

O ejecute la transición en reversa llamando a:

transition.reverseTransition(transitionTime);

Vea la respuesta de Roman para otra solución usando la API de animación de propiedades, que no estaba disponible en el momento en que esta respuesta se publicó originalmente.

idolatrar
fuente
3
Gracias mxrider! Esto responde la pregunta. Desafortunadamente, no funciona para mí porque la vista que tiene TransitionDrawable como fondo también debe estar animada, y parece que la animación de la vista anula la transición del fondo. ¿Algunas ideas?
hpique
66
Lo resolvió iniciando TransitionDrawable DESPUÉS de comenzar la animación.
hpique
2
¡Increíble! ¡Me alegra que lo hayas hecho funcionar! Hay muchas cosas interesantes que puedes hacer en xml con Android, muchas de las cuales están un poco enterradas en los documentos y no son obvias en mi opinión.
idolatra el
44
No es obvio en absoluto. Debo mencionar que no estoy usando xml en mi caso, ya que el color es dinámico. Su solución también funciona si crea TransitionDrawable por código.
hpique
2
Uno de los principales inconvenientes es que no hay un oyente completo de animación
Leo Droidcoder
511

Puede usar la nueva API de animación de propiedades para la animación en color:

int colorFrom = getResources().getColor(R.color.red);
int colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.setDuration(250); // milliseconds
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animator) {
        textView.setBackgroundColor((int) animator.getAnimatedValue());
    }

});
colorAnimation.start();

Para compatibilidad con versiones anteriores de Android 2.x, use la biblioteca Nine Old Androids de Jake Wharton.

El getColormétodo fue desaprobado en Android M, por lo que tiene dos opciones:

  • Si usa la biblioteca de soporte, debe reemplazar las getColorllamadas con:

    ContextCompat.getColor(this, R.color.red);
  • Si no utiliza la biblioteca de soporte, debe reemplazar las getColorllamadas con:

    getColor(R.color.red);
Roman Minenok
fuente
2
Solución "imprescindible" si está utilizando la API de animación de propiedades (o NineOldAndroid). Anima muy suavemente.
Dmitry Zaytsev
1
¿Hay alguna forma de establecer la duración de la transición de esta manera?
iGio90
159
@ iGio90 sí, no lo vas a creer pero el método llama a setDuration () :)
Roman Minenok
66
ValueAnimator.ofArgb(colorFrom, colorTo)parece más al punto, pero lamentablemente es nuevo.
TWiStErRob
1
Hermosa solución! Gracias.
Steve Folly
137

Dependiendo de cómo su vista obtiene su color de fondo y cómo obtiene su color de destino, hay varias formas diferentes de hacerlo.

Los dos primeros usan la animación de propiedad de Android marco de .

Usar una animador de objetos si:

  • Su vista tiene su color de fondo definido como argb valor en un archivo xml.
  • Su vista ha tenido previamente su color establecido por view.setBackgroundColor()
  • Su vista tiene su color de fondo definido en un dibujo que NO define ninguna propiedad adicional como el trazo o los radios de las esquinas.
  • Su vista tiene su color de fondo definido en un dibujable y desea eliminar cualquier propiedad adicional como el trazo o el radio de esquina, tenga en cuenta que la eliminación de las propiedades adicionales no será animada.

El animador de objetos funciona mediante una llamada view.setBackgroundColorque reemplaza el dibujo definido a menos que sea una instancia de unColorDrawable , que rara vez lo es. Esto significa que se eliminarán las propiedades de fondo adicionales de un trazo como trazos o esquinas.

Use un Value Animator si:

  • Su vista tiene su color de fondo definido en un dibujo que también establece propiedades como el trazo o los radios de esquina Y desea cambiarlo a un nuevo color que se decida mientras se ejecuta.

Use una transición dibujable si:

  • Su vista debe cambiar entre dos dibujables que se han definido antes de la implementación.

He tenido algunos problemas de rendimiento con los elementos de transición de Transition que se ejecutan mientras estoy abriendo un DrawerLayout que no he podido resolver, por lo que si encuentra un tartamudeo inesperado, es posible que se haya encontrado con el mismo error que yo.

Tendrá que modificar el ejemplo de Value Animator si desea usar un dibujo de StateLists o un dibujo de LayerLists , de lo contrario se bloqueará en la final GradientDrawable background = (GradientDrawable) view.getBackground();línea.

Animador de objetos :

Ver definición:

<View
    android:background="#FFFF0000"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

Crea y usa un ObjectAnimatorcomo este.

final ObjectAnimator backgroundColorAnimator = ObjectAnimator.ofObject(view,
                                                                       "backgroundColor",
                                                                       new ArgbEvaluator(),
                                                                       0xFFFFFFFF,
                                                                       0xff78c5f9);
backgroundColorAnimator.setDuration(300);
backgroundColorAnimator.start();

También puede cargar la definición de animación desde un xml usando un AnimatorInflater como lo hace XMight en un objeto Android Fondo animado del animador Color de diseño

Animador de valor :

Ver definición:

<View
    android:background="@drawable/example"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

Definición dibujable:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <stroke
        android:color="#edf0f6"
        android:width="1dp"/>
    <corners android:radius="3dp"/>

</shape>

Cree y use un ValueAnimator como este:

final ValueAnimator valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(),
                                                           0xFFFFFFFF,
                                                           0xff78c5f9);

final GradientDrawable background = (GradientDrawable) view.getBackground();
currentAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(final ValueAnimator animator) {
        background.setColor((Integer) animator.getAnimatedValue());
    }

});
currentAnimation.setDuration(300);
currentAnimation.start();

Transición dibujable :

Ver definición:

<View
    android:background="@drawable/example"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

Definición dibujable:

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="#FFFFFF"/>
            <stroke
                android:color="#edf0f6"
                android:width="1dp"/>
            <corners android:radius="3dp"/>
        </shape>
    </item>

    <item>
        <shape>
            <solid android:color="#78c5f9"/>
            <stroke
                android:color="#68aff4"
                android:width="1dp"/>
            <corners android:radius="3dp"/>
        </shape>
    </item>
</transition>

Use el TransitionDrawable de esta manera:

final TransitionDrawable background = (TransitionDrawable) view.getBackground();
background.startTransition(300);

Puedes revertir las animaciones llamando .reverse() a la instancia de animación.

Hay otras formas de hacer animaciones, pero estas tres son probablemente las más comunes. Generalmente uso un ValueAnimator.

Mattias
fuente
En realidad, es posible definir y usar TransitionDrawable en el código, pero por alguna razón, no funciona la segunda vez. extraño.
Desarrollador de Android
Puedo usar el animador de objetos incluso cuando mi vista tiene configurado ColorDrawable. Simplemente lo guardo en una variable local, lo configuro como nulo y cuando la animación termina, vuelvo a configurar el ColorDrawable original.
Miloš Černilovský
55

Puedes hacer un animador de objetos. Por ejemplo, tengo un targetView y quiero cambiar el color de fondo:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();
ademar111190
fuente
2
Desde API 21, puede usar ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Su implementación es justa ofInt(...).setEvaluator(new ArgbEvaluator()).
ypresto
20

Si quieres una animación en color como esta,

Ingrese la descripción de la imagen aquí

este código te ayudará a:

ValueAnimator anim = ValueAnimator.ofFloat(0, 1);   
anim.setDuration(2000);

float[] hsv;
int runColor;
int hue = 0;
hsv = new float[3]; // Transition color
hsv[1] = 1;
hsv[2] = 1;
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {

        hsv[0] = 360 * animation.getAnimatedFraction();

        runColor = Color.HSVToColor(hsv);
        yourView.setBackgroundColor(runColor);
    }
});

anim.setRepeatCount(Animation.INFINITE);

anim.start();
RBK
fuente
99
¿Dónde está la parte cuando se inicializa la variable anim ?
VoW
@VoW, es solo una instancia de ValueAnimator.
RBK
No debería ser anim.setRepeatCount(ValueAnimator.INFINITE);Animación
INFINITO
@MikhailKrishtop ¿qué le pasa a Animation.INFINITE? developer.android.com/reference/android/view/animation/…
RBK
14

la mejor manera es usar ValueAnimator yColorUtils.blendARGB

 ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
 valueAnimator.setDuration(325);
 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {

              float fractionAnim = (float) valueAnimator.getAnimatedValue();

              view.setBackgroundColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF")
                                    , Color.parseColor("#000000")
                                    , fractionAnim));
        }
});
valueAnimator.start();
Rasoul Miri
fuente
12

Otra forma fácil de lograr esto es realizar un desvanecimiento usando AlphaAnimation.

  1. Haga de su vista un ViewGroup
  2. Agregue una vista secundaria en el índice 0, con dimensiones de diseño match_parent
  3. Déle a su hijo el mismo fondo que el contenedor
  4. Cambiar al fondo del contenedor al color de destino
  5. Desvanece al niño usando AlphaAnimation.
  6. Retire al niño cuando la animación esté completa (usando un AnimationListener)
Stephane JAIS
fuente
9

Este es el método que uso en una Actividad base para cambiar el fondo. Estoy usando GradientDrawables generados en código, pero podría adaptarse para adaptarse.

    protected void setPageBackground(View root, int type){
        if (root!=null) {
            Drawable currentBG = root.getBackground();
            //add your own logic here to determine the newBG 
            Drawable newBG = Utils.createGradientDrawable(type); 
            if (currentBG==null) {
                if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN){
                    root.setBackgroundDrawable(newBG);
                }else{
                    root.setBackground(newBG);
                }
            }else{
                TransitionDrawable transitionDrawable = new TransitionDrawable(new Drawable[]{currentBG, newBG});
                transitionDrawable.setCrossFadeEnabled(true);
                if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN){
                     root.setBackgroundDrawable(transitionDrawable);
                }else{
                    root.setBackground(transitionDrawable);
                }
                transitionDrawable.startTransition(400);
            }
        }
    }

actualización: en caso de que alguien se encuentre con el mismo problema que encontré, por alguna razón en Android <4.3 usando setCrossFadeEnabled(true)causa un efecto de blanqueo no deseado, por lo que tuve que cambiar a un color sólido para <4.3 usando el método @Roman Minenok ValueAnimator mencionado anteriormente.

scottyab
fuente
Esto es exactamente lo que estaba buscando. ¡Gracias! :)
cyph3r
7

La respuesta se da de muchas maneras. También se puede utilizar ofArgb(startColor,endColor)de ValueAnimator.

para API> 21:

int cyanColorBg = ContextCompat.getColor(this,R.color.cyan_bg);
int purpleColorBg = ContextCompat.getColor(this,R.color.purple_bg);

ValueAnimator valueAnimator = ValueAnimator.ofArgb(cyanColorBg,purpleColorBg);
        valueAnimator.setDuration(500);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
              @Override
              public void onAnimationUpdate(ValueAnimator valueAnimator) {
                   relativeLayout.setBackgroundColor((Integer)valueAnimator.getAnimatedValue());
              }
        });
        valueAnimator.start();
Palak Darji
fuente
7

La documentación sobre animaciones basadas en XML es horrible. He buscado alrededor de horas solo para animar el color de fondo de un botón al presionar ... Lo triste es que la animación está a solo un atributo de distancia: puede usar exitFadeDurationen selector:

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:exitFadeDuration="200">

    <item android:state_pressed="true">
        <shape android:tint="#3F51B5" />
    </item>

    <item>
        <shape android:tint="#F44336" />
    </item>

</selector>

Luego, utilícelo como backgroundpara su vista. No se necesita código Java / Kotlin.

Mario
fuente
Salvaste mi día. Gracias.
Federico Heiland
1
Excelente respuesta Tenga en cuenta que también puede necesitarlo android:enterFadeDuration; mi experiencia fue que, sin ella, mi animación no funcionó la segunda vez.
Cadena
7

Aquí hay una buena función que permite esto:

public static void animateBetweenColors(final @NonNull View viewToAnimateItsBackground, final int colorFrom,
                                        final int colorTo, final int durationInMs) {
    final ColorDrawable colorDrawable = new ColorDrawable(durationInMs > 0 ? colorFrom : colorTo);
    ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable);
    if (durationInMs > 0) {
        final ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
        colorAnimation.addUpdateListener(animator -> {
            colorDrawable.setColor((Integer) animator.getAnimatedValue());
            ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable);
        });
        colorAnimation.setDuration(durationInMs);
        colorAnimation.start();
    }
}

Y en Kotlin:

@JvmStatic
fun animateBetweenColors(viewToAnimateItsBackground: View, colorFrom: Int, colorTo: Int, durationInMs: Int) {
    val colorDrawable = ColorDrawable(if (durationInMs > 0) colorFrom else colorTo)
    ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable)
    if (durationInMs > 0) {
        val colorAnimation = ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo)
        colorAnimation.addUpdateListener { animator: ValueAnimator ->
            colorDrawable.color = (animator.animatedValue as Int)
            ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable)
        }
        colorAnimation.duration = durationInMs.toLong()
        colorAnimation.start()
    }
}
desarrollador de Android
fuente
esto no es compatible con teléfonos de versión de gama
baja
2
@Kartheeks te refieres a Android 10 o inferior? si es así, puede usar fácilmente una biblioteca "nineOldAndroids" para admitirlo : nineoldandroids.com . La sintaxis es casi exactamente la misma.
Desarrollador de Android
para la versión inferior colorAnimation.addUpdateListener (nuevo ValueAnimator.AnimatorUpdateListener () {@Override public void onAnimationUpdate (ValueAnimator animation) {colorDrawable.setColor ((Integer) animation.getAnimatedValue ()); ViewCompat.setBackground) ;
Mahbubur Rahman Khan
5

agregue un animador de carpeta a la carpeta res . (el nombre debe ser animador ). Agregue un archivo de recursos de animador. Por ejemplo res / animator / fade.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:propertyName="backgroundColor"
        android:duration="1000"
        android:valueFrom="#000000"
        android:valueTo="#FFFFFF"
        android:startOffset="0"
        android:repeatCount="-1"
        android:repeatMode="reverse" />
</set>

Dentro del archivo Java de actividad, llame a esto

View v = getWindow().getDecorView().findViewById(android.R.id.content);
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.fade);
set.setTarget(v);
set.start();
Canbax
fuente
3

Use la siguiente función para Kotlin:

private fun animateColorValue(view: View) {
    val colorAnimation =
        ValueAnimator.ofObject(ArgbEvaluator(), Color.GRAY, Color.CYAN)
    colorAnimation.duration = 500L
    colorAnimation.addUpdateListener { animator -> view.setBackgroundColor(animator.animatedValue as Int) }
    colorAnimation.start()
}

Pase cualquier vista de la que desee cambiar el color.

Esbirro
fuente
2

Descubrí que la implementación utilizada ArgbEvaluatoren el código fuente de Android funciona mejor en la transición de colores. Al usar HSV, dependiendo de los dos colores, la transición fue saltar a través de demasiados tonos para mí. Pero este método no lo es.

Si usted está tratando de animar simplemente, el uso ArgbEvaluatorcon ValueAnimatorcomo se sugiere aquí :

ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animator) {
        view.setBackgroundColor((int) animator.getAnimatedValue());
    }

});
colorAnimation.start();

Sin embargo, si usted es como yo y desea vincular su transición con algún gesto del usuario u otro valor pasado de una entrada, esto ValueAnimatorno es de mucha ayuda (a menos que esté apuntando a la API 22 y superior, en cuyo caso puede usar el ValueAnimator.setCurrentFraction()método ) Cuando apunte por debajo de API 22, ajuste el código encontrado en el ArgbEvaluatorcódigo fuente en su propio método, como se muestra a continuación:

public static int interpolateColor(float fraction, int startValue, int endValue) {
    int startA = (startValue >> 24) & 0xff;
    int startR = (startValue >> 16) & 0xff;
    int startG = (startValue >> 8) & 0xff;
    int startB = startValue & 0xff;
    int endA = (endValue >> 24) & 0xff;
    int endR = (endValue >> 16) & 0xff;
    int endG = (endValue >> 8) & 0xff;
    int endB = endValue & 0xff;
    return ((startA + (int) (fraction * (endA - startA))) << 24) |
            ((startR + (int) (fraction * (endR - startR))) << 16) |
            ((startG + (int) (fraction * (endG - startG))) << 8) |
            ((startB + (int) (fraction * (endB - startB))));
}

Y úsalo como quieras.

fahmy
fuente
0

Basado en la respuesta de ademar111190 , he creado este método que emitirá el color de fondo de una vista entre dos colores:

private void animateBackground(View view, int colorFrom, int colorTo, int duration) {


    ObjectAnimator objectAnimator = ObjectAnimator.ofObject(view, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo);
    objectAnimator.setDuration(duration);
    //objectAnimator.setRepeatCount(Animation.INFINITE);
    objectAnimator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
            // Call this method again, but with the two colors switched around.
            animateBackground(view, colorTo, colorFrom, duration);
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    objectAnimator.start();
}
prohibición de geoingeniería
fuente