Animare il cambiamento del colore di sfondo della vista su Android


335

Come si anima il cambiamento del colore di sfondo di una vista su Android?

Per esempio:

Ho una vista con un colore di sfondo rosso. Il colore di sfondo della vista diventa blu. Come posso effettuare una transizione graduale tra i colori?

Se ciò non è possibile con le viste, sarà gradita un'alternativa.


11
Ci si aspetterebbe che una transizione graduale tra i colori non dovrebbe essere un problema per una piattaforma come Android. Forse le viste non sono costruite per questo, però. La domanda è ancora valida.
— hpique

Risposte:


374

Ho finito per capire una soluzione (abbastanza buona) per questo problema!

È possibile utilizzare un TransitionDrawable per ottenere questo risultato. Ad esempio, in un file XML nella cartella disegnabile potresti scrivere qualcosa del tipo:

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

Quindi, nel tuo XML per la visualizzazione effettiva, faresti riferimento a questo TransitionDrawable android:backgroundnell'attributo.

A questo punto puoi iniziare la transizione nel tuo codice a comando facendo:

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

Oppure esegui la transizione al contrario chiamando:

transition.reverseTransition(transitionTime);

Vedi la risposta di Roman per un'altra soluzione che utilizza l'API di animazione della proprietà, che non era disponibile al momento della pubblicazione della risposta.


3
Grazie mxrider! Questo risponde alla domanda. Sfortunatamente non funziona per me perché anche la vista che ha TransitionDrawable come sfondo deve essere animata, e sembra che l'animazione della vista abbia la precedenza sulla transizione di sfondo. Qualche idea?
— hpique

6
Risolto avviando TransitionDrawable DOPO aver avviato l'animazione.
— hpique

2
Eccezionale! Sono contento che tu abbia funzionato! Ci sono un sacco di cose interessanti che puoi fare in XML con Android - molte delle quali sono un po 'sepolte nei documenti e non sono ovvie secondo me.
— Idolatra il

4
Per niente ovvio. Dovrei menzionare che non sto usando XML nel mio caso, poiché il colore è dinamico. La tua soluzione funziona anche se crei TransitionDrawable per codice.
— hpique

2
Uno degli svantaggi principali è che non esiste un ascoltatore completo di animazione
— Leo Droidcoder

511

È possibile utilizzare la nuova API di animazione della proprietà per l'animazione a colori:

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

Per compatibilità con le versioni precedenti di Android 2.x, utilizzare la libreria Nine Old Androids di Jake Wharton.

Il getColormetodo è stato deprecato in Android M, quindi hai due possibilità:

  • Se si utilizza la libreria di supporto, è necessario sostituire le getColorchiamate con:

    ContextCompat.getColor(this, R.color.red);
  • se non si utilizza la libreria di supporto, è necessario sostituire le getColorchiamate con:

    getColor(R.color.red);

2
soluzione "musthave" se si utilizza l'API di animazione della proprietà (o NineOldAndroid). Anima in modo molto fluido.
— Dmitry Zaytsev,

1
C'è un modo per impostare la durata della transizione in questo modo?
— iGio90,

159
@ iGio90 sì, non ci crederai, ma il metodo chiama setDuration () :)
— Roman Minenok,

6
ValueAnimator.ofArgb(colorFrom, colorTo)sembra più al punto, ma purtroppo è nuovo.
— TWiStErRob

1
Bella soluzione! Grazie.
— Steve Folly,

137

A seconda di come la vista ottiene il colore di sfondo e di come si ottiene il colore target, esistono diversi modi per farlo.

I primi due utilizzano il framework di animazione delle proprietà Android .

Utilizzare un animatore di oggetti se:

  • La vista ha il colore di sfondo definito come argbvalore in un file XML.
  • In precedenza la tua vista aveva il colore impostato view.setBackgroundColor()
  • La tua vista ha il suo colore di sfondo definito in un disegno che NON definisce alcuna proprietà extra come i raggi di tratto o angolo.
  • La vista ha il colore di sfondo definito in un disegno e si desidera rimuovere eventuali proprietà extra come i raggi di tratto o angolo, tenere presente che la rimozione delle proprietà extra non verrà animata.

L'animatore di oggetti funziona chiamando view.setBackgroundColorsostituendo il disegnabile definito a meno che non sia un'istanza di a ColorDrawable, cosa che raramente lo è. Ciò significa che verranno rimosse eventuali proprietà di sfondo aggiuntive da un disegno come tratto o angoli.

Utilizzare un animatore valore se:

  • La tua vista ha il suo colore di sfondo definito in un disegno che imposta anche proprietà come i raggi del tratto o dell'angolo E vuoi cambiarlo con un nuovo colore che viene deciso durante l'esecuzione.

Usa un disegno Transizione se:

  • La vista dovrebbe passare tra due disegni che sono stati definiti prima della distribuzione.

Ho avuto alcuni problemi di prestazioni con i drawable Transition che vengono eseguiti mentre apro un DrawerLayout che non sono stato in grado di risolvere, quindi se si verificano balbuzie impreviste, è possibile che si sia verificato lo stesso errore.

Dovrai modificare l'esempio di Value Animator se vuoi usare un disegnabile StateLists o un LayerList , altrimenti andrà in crash sulla final GradientDrawable background = (GradientDrawable) view.getBackground();linea.

Animatore oggetti :

Vedi definizione:

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

Crea e usa un ObjectAnimatorsimile.

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

Puoi anche caricare la definizione di animazione da un xml usando un AnimatorInflater come XMight fa nell'oggetto Android Animator animare lo sfondo Colore del layout

Value Animator :

Vedi definizione:

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

Definizione estraibile:

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

Crea e usa un ValueAnimator in questo modo:

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

Disegnabile di transizione :

Vedi definizione:

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

Definizione estraibile:

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

Usa TransitionDrawable in questo modo:

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

Puoi invertire le animazioni chiamando .reverse()l'istanza dell'animazione.

Esistono altri modi per realizzare animazioni, ma questi tre sono probabilmente i più comuni. In genere utilizzo un ValueAnimator.


in realtà è possibile definire e utilizzare TransitionDrawable nel codice, ma per qualche motivo, non funziona la seconda volta. strano.
— sviluppatore Android

Sono in grado di utilizzare l'animatore oggetto anche quando la mia vista ha ColorDrawable impostato. L'ho appena salvato in una variabile locale, impostato su null e al termine dell'animazione, ripristina ColorDrawable originale.
— Miloš Černilovský,

55

Puoi creare un animatore di oggetti. Ad esempio, ho un targetView e voglio cambiare il colore di sfondo:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();

2
Da API 21, puoi usare ofArgb(targetView, "backgroundColor", colorFrom, colorTo). La sua implementazione è giusta ofInt(...).setEvaluator(new ArgbEvaluator()).
— venerdì

20

Se vuoi un'animazione a colori come questa,

Inserisci qui la descrizione dell'immagine

questo codice ti aiuterà:

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

9
Dov'è la parte quando viene inizializzata la variabile anim ?
— VoW,

@VoW, è solo un'istanza di ValueAnimator.
— RBK,

Non dovrebbe essere anim.setRepeatCount(ValueAnimator.INFINITE);Animation.INFINITE per ora è lo stesso, ma non abbiamo garanzie
— MikhailKrishtop

@MikhailKrishtop cosa c'è che non va in Animation.INFINITE? developer.android.com/reference/android/view/animation/…
— RBK

14

il modo migliore è usare ValueAnimator eColorUtils.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();

12

Un altro modo semplice per raggiungere questo obiettivo è eseguire una dissolvenza usando AlphaAnimation.

  1. Trasforma la tua vista in un gruppo di visualizzazione
  2. Aggiungi una vista figlio all'indice 0, con dimensioni del layout match_parent
  3. Dai a tuo figlio lo stesso sfondo del contenitore
  4. Passa allo sfondo del contenitore nel colore target
  5. Dissolvenza del bambino usando AlphaAnimation.
  6. Rimuovi il figlio quando l'animazione è completa (usando un AnimationListener)

9

Questo è il metodo che uso in un'attività di base per cambiare lo sfondo. Sto usando GradientDrawables generati nel codice, ma potrebbero essere adattati per soddisfare.

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

aggiornamento: nel caso in cui qualcuno riscontri lo stesso problema che ho riscontrato, per qualche motivo su Android <4.3 l'utilizzo setCrossFadeEnabled(true)causa un effetto sbiadimento indesiderato, quindi ho dovuto passare a un colore solido per <4.3 utilizzando il metodo @Roman Minenok ValueAnimator sopra indicato.


Questo e 'esattamente quello che stavo cercando. Grazie! :)
— cyph3r,

7

La risposta è data in molti modi. Puoi anche usare ofArgb(startColor,endColor)di ValueAnimator.

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

7

La documentazione sulle animazioni basate su XML è orribile. Ho cercato per ore solo per animare il colore di sfondo di un pulsante quando si preme ... La cosa triste è che l'animazione ha solo un attributo: Puoi usare exitFadeDurationin 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>

Quindi usalo come backgroundper la tua vista. Nessun codice Java / Kotlin necessario.


Mi hai salvato la giornata. Grazie.
— Federico Heiland,

1
Risposta eccellente. Si noti che potrebbe essere necessario android:enterFadeDurationanche; la mia esperienza è stata che, senza di essa, la mia animazione non funzionava la seconda volta.
— String

7

Ecco una bella funzione che consente questo:

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

E a 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()
    }
}

questo non supporta i telefoni con versione di fascia bassa
— Kartheek s

2
@Kartheeks intendi Android 10 e precedenti? in tal caso, puoi facilmente utilizzare una libreria "nineOldAndroids" per supportarla: nineoldandroids.com . la sintassi è quasi esattamente la stessa.
— sviluppatore Android

per la versione precedente colorAnimation.addUpdateListener (new ValueAnimator.AnimatorUpdateListener () {@Override public void onAnimationUpdate (ValueAnimator animation) {colorDrawable.setColor ((Integer) animation.getAnimatedValue ()) ColorDack ;
— Mahbubur Rahman Khan,

5

aggiungere un animatore di cartelle nella cartella res . (il nome deve essere animatore ). Aggiungi un file di risorse dell'animatore. Ad esempio 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>

All'interno del file java Activity, chiama questo

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

3

Utilizzare la funzione seguente per 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()
}

Passa la vista di cui vuoi cambiare il colore.


2

Ho scoperto che l'implementazione utilizzata ArgbEvaluatornel codice sorgente di Android fa il miglior lavoro nella transizione dei colori. Quando si utilizza HSV, a seconda dei due colori, la transizione stava saltando attraverso troppe tonalità per me. Ma questo metodo no.

Se stai cercando di animare semplicemente, usa ArgbEvaluatorcon ValueAnimatorcome suggerito qui :

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

Tuttavia, se sei come me e vuoi legare la tua transizione con un gesto dell'utente o un altro valore trasmesso da un input, ciò ValueAnimatornon è di grande aiuto (a meno che tu non stia targetizzando l'API 22 e successive, nel qual caso puoi utilizzare il ValueAnimator.setCurrentFraction()metodo ). Quando scegli come target l'API 22, inserisci il codice trovato nel ArgbEvaluatorcodice sorgente nel tuo metodo, come mostrato di seguito:

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

E usalo come desideri.


0

Sulla base della risposta di ademar111190 , ho creato questo metodo per pulsare il colore di sfondo di una vista tra due colori:

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();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.