Animer le changement de couleur d'arrière-plan de la vue sur Android

335

Comment animez-vous le changement de couleur d'arrière-plan d'une vue sur Android?

Par exemple:

J'ai une vue avec une couleur de fond rouge. La couleur d'arrière-plan de la vue devient bleu. Comment puis-je effectuer une transition en douceur entre les couleurs?

Si cela ne peut pas être fait avec des vues, une alternative sera la bienvenue.

hpique
la source
11
On pourrait s'attendre à ce qu'une transition en douceur entre les couleurs ne soit pas un problème pour une plate-forme comme Android. Peut-être que les vues ne sont pas conçues pour cela. La question se pose toujours.
hpique
1
Un bon tutoriel vidéo
Alex Jolig

Réponses:

374

J'ai fini par trouver une (assez bonne) solution à ce problème!

Vous pouvez utiliser un TransitionDrawable pour accomplir cela. Par exemple, dans un fichier XML dans le dossier dessinable, vous pouvez écrire quelque chose comme:

<?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>

Ensuite, dans votre XML pour la vue réelle, vous référenceriez ce TransitionDrawable dans l' android:backgroundattribut.

À ce stade, vous pouvez lancer la transition dans votre code sur commande en faisant:

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

Ou exécutez la transition en sens inverse en appelant:

transition.reverseTransition(transitionTime);

Voir la réponse de Roman pour une autre solution utilisant l'API d'animation de propriété, qui n'était pas disponible au moment où cette réponse a été initialement publiée.

idolâtrer
la source
3
Merci mxrider! Cela répond à la question. Malheureusement, cela ne fonctionne pas pour moi car la vue qui a le TransitionDrawable en arrière-plan doit également être animée, et il semble que l'animation de la vue remplace la transition en arrière-plan. Des idées?
hpique
6
Résolu en démarrant le TransitionDrawable APRÈS avoir démarré l'animation.
hpique
2
Impressionnant! Heureux que tu es parvenu à le faire fonctionner! Il y a beaucoup de trucs sympas que vous pouvez faire en XML avec Android - dont une grande partie est enterrée dans les documents et pas évident à mon avis.
idolâtrer le
4
Pas évident du tout. Je dois mentionner que je n'utilise pas xml dans mon cas, car la couleur est dynamique. Votre solution fonctionne également si vous créez le TransitionDrawable par code.
hpique
2
L'un des principaux inconvénients est qu'il n'y a pas d'auditeur complet d'animation
Leo Droidcoder
511

Vous pouvez utiliser une nouvelle API d'animation de propriété pour l'animation des couleurs:

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();

Pour une compatibilité descendante avec Android 2.x, utilisez la bibliothèque Nine Old Androids de Jake Wharton.

La getColorméthode a été déconseillée dans Android M, vous avez donc deux choix:

  • Si vous utilisez la bibliothèque de support, vous devez remplacer les getColorappels par:

    ContextCompat.getColor(this, R.color.red);
  • si vous n'utilisez pas la bibliothèque de support, vous devez remplacer les getColorappels par:

    getColor(R.color.red);
Roman Minenok
la source
2
solution "musthave" si vous utilisez l'API Property Animation (ou NineOldAndroid). S'anime très bien.
Dmitry Zaytsev
1
Existe-t-il un moyen de définir la durée de la transition de cette manière?
iGio90
159
@ iGio90 oui, tu ne vas pas y croire mais la méthode appelle setDuration () :)
Roman Minenok
6
ValueAnimator.ofArgb(colorFrom, colorTo)semble plus pertinent, mais malheureusement, c'est nouveau.
TWiStErRob
1
Belle solution! Je vous remercie.
Steve Folly
137

Selon la façon dont votre vue obtient sa couleur d'arrière-plan et la façon dont vous obtenez votre couleur cible, il existe plusieurs façons de procéder.

Les deux premiers utilisent le framework Android Property Animation .

Utilisez un Animateur d'objets si:

  • Votre vue a sa couleur d'arrière-plan définie comme une argbvaleur dans un fichier xml.
  • Votre couleur a déjà été définie par view.setBackgroundColor()
  • Votre vue a sa couleur d'arrière-plan définie dans un dessinable qui NE définit PAS de propriétés supplémentaires comme les rayons de contour ou d'angle.
  • Votre vue a sa couleur d'arrière-plan définie dans un dessin et vous souhaitez supprimer toutes les propriétés supplémentaires comme les rayons de contour ou d'angle, gardez à l'esprit que la suppression des propriétés supplémentaires ne sera pas animée.

L'animateur d'objet fonctionne en appelant view.setBackgroundColorce qui remplace le dessinable défini, sauf s'il s'agit d'une instance de a ColorDrawable, ce qui est rarement le cas. Cela signifie que toutes les propriétés d'arrière-plan supplémentaires d'un trait comme les traits ou les coins peuvent être supprimées.

Utilisez un animateur de valeur si:

  • Votre vue a sa couleur d'arrière-plan définie dans un dessin qui définit également des propriétés telles que le rayon de contour ou de coin ET vous souhaitez la changer en une nouvelle couleur qui est décidée lors de l'exécution.

Utilisez une transition à dessiner si:

  • Votre vue doit basculer entre deux dessinables qui ont été définis avant le déploiement.

J'ai eu des problèmes de performances avec les drawables Transition qui s'exécutent pendant que j'ouvre un DrawerLayout que je n'ai pas pu résoudre, donc si vous rencontrez un bégaiement inattendu, vous pourriez avoir rencontré le même bogue que moi.

Vous devrez modifier l'exemple Value Animator si vous souhaitez utiliser un dessin StateLists ou un LayerLists , sinon il se plantera sur la final GradientDrawable background = (GradientDrawable) view.getBackground();ligne.

Animateur d'objets :

Définition de la vue:

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

Créez et utilisez un ObjectAnimatorcomme ceci.

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

Vous pouvez également charger la définition d'animation à partir d'un fichier XML à l'aide d'un AnimatorInflater comme XMight le fait dans un objet AndroidAnimator animate backgroundColor of Layout

Animateur de valeur :

Définition de la vue:

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

Définition dessinable:

<?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>

Créez et utilisez un ValueAnimator comme celui-ci:

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();

Transition drawable :

Définition de la vue:

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

Définition dessinable:

<?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>

Utilisez le TransitionDrawable comme ceci:

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

Vous pouvez inverser les animations en appelant .reverse()l'instance d'animation.

Il existe d'autres façons de faire des animations, mais ces trois sont probablement les plus courantes. J'utilise généralement un ValueAnimator.

Mattias
la source
en fait, il est possible de définir et d'utiliser le TransitionDrawable dans le code, mais pour une raison quelconque, cela ne fonctionne pas la deuxième fois. bizarre.
développeur Android
Je peux utiliser l'animateur d'objet même lorsque ma vue a défini ColorDrawable. Je viens de l'enregistrer dans une variable locale, de le définir sur null et lorsque l'animation est terminée, de rétablir le ColorDrawable d'origine.
Miloš Černilovský
55

Vous pouvez créer un animateur d'objets. Par exemple, j'ai un targetView et je veux changer la couleur de votre fond:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();
ademar111190
la source
2
Depuis l'API 21, vous pouvez utiliser ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Sa mise en œuvre est juste ofInt(...).setEvaluator(new ArgbEvaluator()).
ypresto
20

Si vous voulez une animation couleur comme celle-ci,

Entrez la description de l'image ici

ce code vous aidera:

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
la source
9
Où est la partie lorsque la variable anim est initialisée?
VoW
@VoW, c'est juste une instance de ValueAnimator.
RBK
Ce ne devrait anim.setRepeatCount(ValueAnimator.INFINITE);pas être Animation.INFINITE pour le moment c'est la même chose, mais nous n'avons aucune garantie
MikhailKrishtop
@MikhailKrishtop qu'est-ce qui ne va pas avec Animation.INFINITE? developer.android.com/reference/android/view/animation/…
RBK
14

la meilleure façon est d’utiliser ValueAnimator etColorUtils.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
la source
12

Un autre moyen simple d'y parvenir consiste à effectuer un fondu à l'aide d'AlphaAnimation.

  1. Faites de votre vue un ViewGroup
  2. Ajoutez-y une vue enfant à l'index 0, avec les dimensions de disposition match_parent
  3. Donnez à votre enfant le même arrière-plan que le contenant
  4. Passer à l'arrière-plan du conteneur à la couleur cible
  5. Effacez l'enfant à l'aide d'AlphaAnimation.
  6. Supprimer l'enfant lorsque l'animation est terminée (à l'aide d'un AnimationListener)
Stéphane JAIS
la source
9

C'est la méthode que j'utilise dans une activité de base pour changer l'arrière-plan. J'utilise GradientDrawables généré dans le code, mais pourrait être adapté en fonction.

    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);
            }
        }
    }

mise à jour: au cas où quelqu'un rencontrerait le même problème que j'ai trouvé, pour une raison quelconque sur Android <4.3, l'utilisation setCrossFadeEnabled(true)provoquait un effet de blanc indésirable, j'ai donc dû passer à une couleur unie pour <4.3 en utilisant la méthode @Roman Minenok ValueAnimator susmentionnée.

scottyab
la source
Ceci est exactement ce que je cherchais. Merci! :)
cyph3r
7

La réponse est donnée de plusieurs façons. Vous pouvez également utiliser ofArgb(startColor,endColor)des ValueAnimator.

pour 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
la source
7

La documentation sur les animations propulsées par XML est horrible. J'ai cherché autour des heures juste pour animer la couleur de fond d'un bouton en appuyant sur ... Le plus triste, c'est que l'animation n'est qu'à un attribut: Vous pouvez utiliser exitFadeDurationdans 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>

Ensuite, utilisez-le comme backgroundpour votre vue. Aucun code Java / Kotlin nécessaire.

Marius
la source
Tu m'as sauvé la journée. Merci.
Federico Heiland
1
Excellente réponse. Notez que vous pourriez également avoir besoin android:enterFadeDuration; mon expérience était que, sans elle, mon animation ne fonctionnait pas la deuxième fois.
String
7

Voici une belle fonction qui permet cela:

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();
    }
}

Et à 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()
    }
}
développeur android
la source
cela ne prend pas en charge les téléphones de version inférieure
Kartheek s
2
@Kartheeks, vous voulez dire Android 10 et inférieur? si c'est le cas, vous pouvez facilement utiliser une bibliothèque "nineOldAndroids" pour la prendre en charge: nineoldandroids.com . la syntaxe est presque exactement la même.
développeur Android
pour la version inférieure colorAnimation.addUpdateListener (nouveau ValueAnimator.AnimatorUpdateListener () {@Override public void onAnimationUpdate (animation ValueAnimator) {colorDrawable.setColor ((Integer) animation.getAnimatedValue ()); ViewCompat.setBackground (viewToAnimate), ViewToAnimate ;
Mahbubur Rahman Khan
5

ajoutez un animateur de dossier dans le dossier res . (le nom doit être animateur ). Ajoutez un fichier de ressources d'animation. Par exemple 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>

À l'intérieur du fichier java Activity, appelez ceci

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

Utilisez la fonction ci-dessous pour 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()
}

Passez la vue dont vous souhaitez changer la couleur.

Serviteur
la source
2

J'ai trouvé que l'implémentation utilisée par ArgbEvaluatordans le code source Android fait le meilleur travail dans la transition des couleurs. Lors de l'utilisation de HSV, selon les deux couleurs, la transition sautait à travers trop de teintes pour moi. Mais cette méthode ne fonctionne pas.

Si vous essayez d'animer simplement, utilisez ArgbEvaluatoravec ValueAnimatorcomme suggéré ici :

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();

Cependant, si vous êtes comme moi et que vous souhaitez lier votre transition à un geste de l'utilisateur ou à une autre valeur transmise à partir d'une entrée, cela ValueAnimatorn'est pas très utile (sauf si vous ciblez l'API 22 et au-dessus, auquel cas vous pouvez utiliser la ValueAnimator.setCurrentFraction()méthode ). Lorsque vous ciblez en dessous de l'API 22, enveloppez le code trouvé dans ArgbEvaluatorle code source dans votre propre méthode, comme indiqué ci-dessous:

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))));
}

Et utilisez-le comme vous le souhaitez.

fahmy
la source
0

Sur la base de la réponse de ademar111190 , j'ai créé cette méthode qui pulsera la couleur d'arrière-plan d'une vue entre deux couleurs quelconques:

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();
}
interdiction de la géo-ingénierie
la source