Animuj zmianę koloru tła widoku na Androidzie

335

Jak animować zmianę koloru tła widoku na Androidzie?

Na przykład:

Mam widok z czerwonym kolorem tła. Kolor tła widoku zmienia się na niebieski. Jak mogę płynnie przechodzić między kolorami?

Jeśli nie można tego zrobić z widokami, alternatywa będzie mile widziana.

hpique
źródło
11
Można się spodziewać, że płynne przejście między kolorami nie powinno stanowić problemu dla platformy takiej jak Android. Być może jednak nie są tworzone widoki. Pytanie wciąż jest aktualne.
hpique
1
Dobry samouczek wideo
Alex Jolig,

Odpowiedzi:

374

W końcu znalazłem (całkiem dobre) rozwiązanie tego problemu!

Aby to zrobić, możesz użyć TransitionDrawable . Na przykład w pliku XML w folderze z możliwością rysowania możesz napisać coś takiego:

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

Następnie w swoim XML dla rzeczywistego Widoku odwoływałbyś się do TransitionDrawable w android:backgroundatrybucie.

W tym momencie możesz zainicjować przejście w swoim kodzie na polecenie, wykonując:

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

Lub uruchom przejście w odwrotną stronę, wywołując:

transition.reverseTransition(transitionTime);

Zobacz odpowiedź Romana na inne rozwiązanie korzystające z interfejsu API Animacji właściwości, który nie był dostępny w momencie, gdy ta odpowiedź została pierwotnie opublikowana.

ubóstwiać
źródło
3
Dzięki mxrider! To odpowiada na pytanie. Niestety dla mnie to nie działa, ponieważ widok, który ma TransitionDrawable jako tło, również musi być animowany i wydaje się, że animacja widoku zastępuje przejście tła. Jakieś pomysły?
hpique
6
Rozwiązano go, uruchamiając TransitionDrawable PO uruchomieniu animacji.
hpique
2
Niesamowite! Cieszę się, że to działa! Istnieje wiele fajnych rzeczy, które możesz zrobić w xml na Androidzie - z których większość jest trochę zakopana w dokumentach i moim zdaniem nie jest to oczywiste.
ubóstwiam
4
Wcale nie oczywiste. Powinienem wspomnieć, że nie używam xml w moim przypadku, ponieważ kolor jest dynamiczny. Twoje rozwiązanie działa również, jeśli utworzysz TransitionDrawable według kodu.
hpique
2
Jedną z głównych wad jest to, że nie ma pełnego odbiornika animacji
Leo Droidcoder
511

Do animacji kolorów można użyć nowego interfejsu API animacji właściwości :

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

Aby zachować kompatybilność wsteczną z systemem Android 2.x, użyj biblioteki Nine Old Androidy od Jake Wharton.

getColorMetoda została zaniechana w Android M, więc masz dwie możliwości:

  • Jeśli korzystasz z biblioteki wsparcia, musisz zastąpić getColorpołączenia:

    ContextCompat.getColor(this, R.color.red);
  • jeśli nie korzystasz z biblioteki wsparcia, musisz zastąpić getColorwywołania:

    getColor(R.color.red);
Roman Minenok
źródło
2
rozwiązanie „musthave”, jeśli korzystasz z interfejsu API Animacji właściwości (lub NineOldAndroid). Animuje bardzo płynnie.
Dmitrij Zajcew
1
Czy jest jakiś sposób, aby ustawić czas trwania przejścia w ten sposób?
iGio90
159
@ iGio90 tak, nie uwierzysz, ale wywołanie metody setDuration () :)
Roman Minenok
6
ValueAnimator.ofArgb(colorFrom, colorTo)wygląda bardziej na sedno, ale niestety jest nowy.
TWiStErRob
1
Piękne rozwiązanie! Dziękuję Ci.
Steve Folly
137

W zależności od tego, w jaki sposób widok ma kolor tła i od tego, w jaki sposób uzyskasz kolor docelowy, możesz to zrobić na kilka różnych sposobów.

Pierwsze dwa wykorzystują system Android Property Animation .

Użyj animatora obiektów, jeśli:

  • Twój widok ma zdefiniowany kolor tła jako argbwartość w pliku xml.
  • Twój widok wcześniej miał ustawiony kolor view.setBackgroundColor()
  • Twój widok ma zdefiniowany kolor tła na rysunku, który NIE definiuje żadnych dodatkowych właściwości, takich jak promień obrysu lub narożnika.
  • Twój widok ma zdefiniowany kolor tła w rysunku i chcesz usunąć wszelkie dodatkowe właściwości, takie jak promienie obrysu lub narożnika, pamiętaj, że usunięcie dodatkowych właściwości nie będzie animowane.

Animator obiektów działa poprzez wywołanie, view.setBackgroundColorktóre zastępuje zdefiniowane dające się rysować, chyba że jest to instancja a ColorDrawable, którą rzadko jest. Oznacza to, że wszelkie dodatkowe właściwości tła z rysunku, takie jak obrys lub narożniki, zostaną usunięte.

Użyj animatora wartości, jeśli:

  • Twój widok ma zdefiniowany kolor tła na rysunku, który również ustawia właściwości takie jak promień obrysu lub narożnika ORAZ chcesz zmienić go na nowy kolor, który zostanie określony podczas pracy.

Użyj rysowanego przejścia, jeśli:

  • Twój widok powinien przełączać się między dwiema wersjami, które zostały zdefiniowane przed wdrożeniem.

Miałem pewne problemy z wydajnością programów drawable Transition, które działają podczas otwierania DrawerLayout, których nie byłem w stanie rozwiązać, więc jeśli napotkasz jakieś nieoczekiwane jąkanie, możesz napotkać ten sam błąd, co ja.

Będziesz musiał zmodyfikować przykład Value Animator, jeśli chcesz użyć rysowalnej StateLists lub rysowalnej LayerLists , w przeciwnym razie ulegnie awarii na final GradientDrawable background = (GradientDrawable) view.getBackground();linii.

Animator obiektów :

Zobacz definicję:

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

Utwórz i użyj ObjectAnimatorpodobnego.

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

Możesz także załadować definicję animacji z pliku XML za pomocą AnimatorInflater, tak jak XMight robi w obiekcie Android Animowane tło animacji Kolor układu

Animator wartości :

Zobacz definicję:

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

Rysowana definicja:

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

Utwórz i użyj ValueAnimator w następujący sposób:

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

Wyciągane przejście :

Zobacz definicję:

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

Rysowana definicja:

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

Użyj TransitionDrawable w następujący sposób:

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

Możesz odwrócić animacje, wywołując .reverse()instancję animacji.

Istnieje kilka innych sposobów tworzenia animacji, ale te trzy są prawdopodobnie najczęstsze. Ogólnie używam ValueAnimator.

Mattias
źródło
w rzeczywistości możliwe jest zdefiniowanie i użycie TransitionDrawable w kodzie, ale z jakiegoś powodu nie działa po raz drugi. dziwne.
programista Androida
Jestem w stanie korzystać z animatora obiektu, nawet jeśli mój widok ma ustawiony zestaw ColorDrawable. Po prostu zapisuję go w zmiennej lokalnej, ustawiam na null, a kiedy animacja jest zakończona, przywróć oryginalny ColorDrawable.
Miloš Černilovský
55

Możesz zrobić animator obiektu. Na przykład mam obiekt docelowy i chcę zmienić kolor tła:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();
ademar111190
źródło
2
Z API 21 możesz korzystać ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Jego wdrożenie jest sprawiedliwe ofInt(...).setEvaluator(new ArgbEvaluator()).
ypresto
20

Jeśli chcesz taką animację kolorową,

Wpisz opis zdjęcia tutaj

ten kod pomoże ci:

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
źródło
9
Gdzie jest część przy inicjalizacji zmiennej anim ?
VoW
@VoW, to tylko instancja ValueAnimator.
RBK
Nie powinien to być anim.setRepeatCount(ValueAnimator.INFINITE);Animation.INFINITE, na razie jest taki sam, ale nie mamy żadnych gwarancji
MikhailKrishtop,
@MikhailKrishtop, co jest nie tak z Animation.INFINITE? developer.android.com/reference/android/view/animation/…
RBK
14

najlepszym sposobem jest użycie ValueAnimator iColorUtils.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
źródło
12

Innym łatwym sposobem na osiągnięcie tego jest wykonanie przejścia za pomocą AlphaAnimation.

  1. Zmień widok w grupę ViewGroup
  2. Dodaj do niego widok podrzędny o indeksie 0 z wymiarami układu match_parent
  3. Podaj swojemu dziecku to samo tło co pojemnik
  4. Zmień tło pojemnika na kolor docelowy
  5. Wycisz dziecko za pomocą AlphaAnimation.
  6. Usuń dziecko po zakończeniu animacji (za pomocą AnimationListener)
Stephane JAIS
źródło
9

Jest to metoda, której używam w działaniu podstawowym do zmiany tła. Używam GradientDrawables wygenerowanych w kodzie, ale można je dostosować do potrzeb.

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

aktualizacja: na wypadek, gdyby ktoś napotkał ten sam problem, z jakiegoś powodu na Androidzie <4.3 użycie setCrossFadeEnabled(true)powoduje niepożądany efekt wygaszenia, więc musiałem przełączyć się na jednolity kolor dla <4.3 za pomocą metody @Roman Minenok ValueAnimator wspomnianej powyżej.

szkocja
źródło
Właśnie tego szukałem. Dzięki! :)
cyph3r
7

Odpowiedź udzielana jest na wiele sposobów. Można również skorzystać ofArgb(startColor,endColor)z ValueAnimator.

dla 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
źródło
7

Dokumentacja animacji opartych na XML jest okropna. Szukałem około godzin, aby animować kolor tła przycisku po naciśnięciu ... Smutne jest to, że animacja jest tylko o jeden atrybut: Możesz użyć exitFadeDurationw 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>

Następnie użyj go jak backgrounddo swojego widoku. Kod Java / Kotlin nie jest potrzebny.

Marius
źródło
Uratowałeś mi dzień. Dzięki.
Federico Heiland,
1
Doskonała odpowiedź. Pamiętaj, że możesz także potrzebować android:enterFadeDuration; moje doświadczenie było takie, że bez tego moja animacja nie działała po raz drugi.
String
7

Oto fajna funkcja, która pozwala na to:

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

A w Kotlinie:

@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()
    }
}
programista Androida
źródło
nie obsługuje to telefonów z niższą wersją
Kartheek s
2
@Kartheeks masz na myśli Androida 10 i starsze? jeśli tak, możesz łatwo skorzystać z biblioteki „nineOldAndroids”, która ją obsługuje: nineoldandroids.com . składnia jest prawie dokładnie taka sama.
programista Androida
dla niższej wersji colorAnimation.addUpdateListener (nowa ValueAnimator.AnimatorUpdateListener () {@Override public void onAnimationUpdate (ValueAnimator animation) {colorDrawable.setColor ((Integer) ;
Mahbubur Rahman Khan
5

dodaj animator folderu do folderu res . (nazwa musi być animatorem ). Dodaj plik zasobów animatora. Na przykład 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>

Wewnątrz pliku Java działania, nazwij to

View v = getWindow().getDecorView().findViewById(android.R.id.content);
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.fade);
set.setTarget(v);
set.start();
Canbax
źródło
3

Użyj poniższej funkcji dla 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()
}

Podaj dowolny widok, który chcesz zmienić kolor.

Sługus
źródło
2

Przekonałem się, że implementacja zastosowana ArgbEvaluatorw kodzie źródłowym Androida najlepiej sprawdza się w przenoszeniu kolorów. Podczas korzystania z HSV, w zależności od dwóch kolorów, przejście przeskakiwało dla mnie zbyt wiele odcieni. Ale ta metoda nie.

Jeśli próbujesz po prostu animować, użyj zgodnie ArgbEvaluatorz ValueAnimatorsugestią tutaj :

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

Jednak jeśli jesteś podobny do mnie i chcesz powiązać przejście z jakimś gestem użytkownika lub inną wartością przekazaną z danych wejściowych, ValueAnimatorto nie jest zbyt pomocne (chyba że celujesz w API 22 i wyżej, w którym to przypadku możesz użyć ValueAnimator.setCurrentFraction()metody ). Podczas celowania poniżej API 22, zawiń kod znaleziony w ArgbEvaluatorkodzie źródłowym własną metodą, jak pokazano poniżej:

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

I używaj go, jak chcesz.

fahmy
źródło
0

Na podstawie odpowiedzi ademar111190 stworzyłem tę metodę, która będzie pulsować kolorem tła widoku między dowolnymi dwoma kolorami:

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();
}
zakaz geoinżynierii
źródło