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.