Animieren Sie die Hintergrundfarbe der Ansichtsänderung auf Android

335

Wie animieren Sie die Änderung der Hintergrundfarbe einer Ansicht auf Android?

Zum Beispiel:

Ich habe eine Ansicht mit einer roten Hintergrundfarbe. Die Hintergrundfarbe der Ansicht ändert sich in Blau. Wie kann ich einen reibungslosen Übergang zwischen Farben vornehmen?

Wenn dies mit Ansichten nicht möglich ist, ist eine Alternative willkommen.

hpique
quelle
11
Man würde erwarten, dass ein reibungsloser Übergang zwischen den Farben für eine Plattform wie Android kein Problem sein sollte. Möglicherweise sind die Ansichten jedoch nicht dafür erstellt. Die Frage bleibt bestehen.
Hpique
1
Ein gutes Video-Tutorial
Alex Jolig

Antworten:

374

Am Ende fand ich eine (ziemlich gute) Lösung für dieses Problem!

Sie können dazu ein TransitionDrawable verwenden . In einer XML-Datei im Zeichenordner können Sie beispielsweise Folgendes schreiben:

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

Dann würden Sie in Ihrem XML für die tatsächliche Ansicht auf dieses TransitionDrawable im android:backgroundAttribut verweisen .

An diesem Punkt können Sie den Übergang in Ihrem Code auf Befehl einleiten, indem Sie Folgendes tun:

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

Oder führen Sie den Übergang in umgekehrter Reihenfolge aus, indem Sie Folgendes aufrufen:

transition.reverseTransition(transitionTime);

In der Antwort von Roman finden Sie eine andere Lösung mit der Eigenschaftsanimations-API, die zum Zeitpunkt der ursprünglichen Veröffentlichung dieser Antwort nicht verfügbar war.

vergöttern
quelle
3
Danke mxrider! Dies beantwortet die Frage. Leider funktioniert es bei mir nicht, da die Ansicht mit TransitionDrawable als Hintergrund ebenfalls animiert werden muss und die Ansichtsanimation den Hintergrundübergang überschreibt. Irgendwelche Ideen?
Hpique
6
Es wurde behoben, indem TransitionDrawable NACH dem Starten der Animation gestartet wurde.
Hpique
2
Genial! Ich bin froh, dass du es zum Laufen gebracht hast! Es gibt eine Menge cooler Dinge, die Sie mit Android in XML machen können - viele davon sind in den Dokumenten vergraben und meiner Meinung nach nicht offensichtlich.
vergöttern
4
Überhaupt nicht offensichtlich. Ich sollte erwähnen, dass ich in meinem Fall keine XML verwende, da die Farbe dynamisch ist. Ihre Lösung funktioniert auch, wenn Sie TransitionDrawable per Code erstellen.
HPX
2
Einer der Hauptnachteile ist, dass es keinen vollständigen Animations-Listener gibt
Leo Droidcoder
508

Sie können die neue Eigenschaftsanimations-API für Farbanimationen verwenden:

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

Verwenden Sie für die Abwärtskompatibilität mit Android 2.x die Nine Old Androids-Bibliothek von Jake Wharton.

Die getColorMethode war in Android M veraltet, sodass Sie zwei Möglichkeiten haben:

  • Wenn Sie die Support-Bibliothek verwenden, müssen Sie die getColorAnrufe ersetzen durch:

    ContextCompat.getColor(this, R.color.red);
  • Wenn Sie die Support-Bibliothek nicht verwenden, müssen Sie die getColorAnrufe ersetzen durch:

    getColor(R.color.red);
Roman Minenok
quelle
2
"musthave" -Lösung, wenn Sie die Property Animation API (oder NineOldAndroid) verwenden. Animiert sehr reibungslos.
Dmitry Zaytsev
1
Gibt es eine Möglichkeit, die Dauer des Übergangs auf diese Weise festzulegen?
iGio90
159
@ iGio90 ja, du wirst es nicht glauben, aber die Methode call setDuration () :)
Roman Minenok
6
ValueAnimator.ofArgb(colorFrom, colorTo)sieht eher auf den Punkt aus, ist aber leider neu.
TWiStErRob
1
Schöne Lösung! Vielen Dank.
Steve Folly
137

Abhängig davon, wie Ihre Ansicht ihre Hintergrundfarbe erhält und wie Sie Ihre Zielfarbe erhalten, gibt es verschiedene Möglichkeiten, dies zu tun.

Die ersten beiden verwenden das Android Property Animation Framework.

Verwenden Sie einen Objektanimator, wenn:

  • Die Hintergrundfarbe Ihrer Ansicht ist als argbWert in einer XML-Datei definiert.
  • Die Farbe Ihrer Ansicht wurde zuvor von festgelegt view.setBackgroundColor()
  • Die Hintergrundfarbe Ihrer Ansicht ist in einem Zeichen definiert , das KEINE zusätzlichen Eigenschaften wie Striche oder Eckenradien definiert.
  • Die Hintergrundfarbe Ihrer Ansicht ist in einer Zeichnungsform definiert. Sie möchten zusätzliche Eigenschaften wie Striche oder Eckenradien entfernen. Beachten Sie, dass das Entfernen der zusätzlichen Eigenschaften nicht animiert wird.

Der Objektanimator ruft auf, view.setBackgroundColorder das definierte Drawable ersetzt, es sei denn, es handelt sich um eine Instanz von a ColorDrawable, was selten der Fall ist. Dies bedeutet, dass zusätzliche Hintergrundeigenschaften aus einem Zeichenelement wie Strich oder Ecken entfernt werden.

Verwenden Sie einen Werteanimator, wenn:

  • Die Hintergrundfarbe Ihrer Ansicht wird in einem Zeichenbild definiert, das auch Eigenschaften wie den Strich oder den Eckenradius festlegt UND Sie möchten sie in eine neue Farbe ändern, die während der Ausführung festgelegt wird.

Verwenden Sie eine Übergangszeichnung, wenn:

  • Ihre Ansicht sollte zwischen zwei Zeichen wechseln, die vor der Bereitstellung definiert wurden.

Ich hatte einige Leistungsprobleme mit Transition-Drawables, die ausgeführt werden, während ich ein DrawerLayout öffne, das ich nicht lösen konnte. Wenn Sie also auf unerwartetes Stottern stoßen, sind Sie möglicherweise auf denselben Fehler gestoßen wie ich.

Sie müssen das Beispiel für den Wertanimator ändern , wenn Sie eine Zeichnungsdatei für StateLists oder eine Zeichnungsliste für LayerLists verwenden möchten. Andernfalls stürzt sie in der final GradientDrawable background = (GradientDrawable) view.getBackground();Zeile ab.

Objektanimator :

Ansichtsdefinition:

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

Erstellen und verwenden Sie eine ObjectAnimatorsolche.

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

Sie können die Animationsdefinition auch mit einem AnimatorInflater aus einer XML laden, wie dies XMight in Android objectAnimator tut. Animieren Sie backgroundColor of Layout

Wertanimator :

Ansichtsdefinition:

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

Zeichnungsfähige Definition:

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

Erstellen und verwenden Sie einen ValueAnimator wie folgt:

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

Übergang zeichnbar :

Ansichtsdefinition:

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

Zeichnungsfähige Definition:

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

Verwenden Sie das TransitionDrawable wie folgt:

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

Sie können die Animationen umkehren, indem Sie die Animationsinstanz aufrufen .reverse().

Es gibt einige andere Möglichkeiten, Animationen zu erstellen, aber diese drei sind wahrscheinlich die häufigsten. Ich benutze normalerweise einen ValueAnimator.

Mattias
quelle
Eigentlich ist es möglich, TransitionDrawable im Code zu definieren und zu verwenden, aber aus irgendeinem Grund funktioniert es beim zweiten Mal nicht. seltsam.
Android-Entwickler
Ich kann den Objektanimator auch dann verwenden, wenn für meine Ansicht ColorDrawable festgelegt ist. Ich speichere es einfach in einer lokalen Variablen, setze es auf null und setze nach Abschluss der Animation das ursprüngliche ColorDrawable zurück.
Miloš Černilovský
55

Sie können einen Objektanimator erstellen. Zum Beispiel habe ich eine Zielansicht und möchte Ihre Hintergrundfarbe ändern:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();
ademar111190
quelle
2
Ab API 21 können Sie verwenden ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Die Umsetzung ist gerecht ofInt(...).setEvaluator(new ArgbEvaluator()).
Ypresto
20

Wenn Sie eine solche Farbanimation wünschen,

Geben Sie hier die Bildbeschreibung ein

Dieser Code hilft Ihnen:

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
quelle
9
Wo ist der Teil, in dem die Animationsvariable initialisiert wird?
VoW
@VoW, es ist nur eine Instanz von ValueAnimator.
RBK
Es sollte sein anim.setRepeatCount(ValueAnimator.INFINITE); nicht Animation.INFINITE sein, im
Moment ist
@MikhailKrishtop Was ist los mit Animation.INFINITE? developer.android.com/reference/android/view/animation/…
RBK
14

Am besten verwenden Sie ValueAnimator undColorUtils.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
quelle
12

Eine andere einfache Möglichkeit, dies zu erreichen, besteht darin, eine Überblendung mit AlphaAnimation durchzuführen.

  1. Machen Sie Ihre Ansicht zu einer ViewGroup
  2. Fügen Sie am Index 0 eine untergeordnete Ansicht mit den Layoutabmessungen match_parent hinzu
  3. Geben Sie Ihrem Kind den gleichen Hintergrund wie dem Container
  4. Wechseln Sie zum Hintergrund des Containers zur Zielfarbe
  5. Blenden Sie das Kind mit AlphaAnimation aus.
  6. Entfernen Sie das untergeordnete Element, wenn die Animation abgeschlossen ist (mithilfe eines AnimationListener).
Stephane JAIS
quelle
Das ist ein Trick! gut gemacht . . .
Elyar Abad
9

Dies ist die Methode, die ich in einer Basisaktivität verwende, um den Hintergrund zu ändern. Ich verwende GradientDrawables, die im Code generiert wurden, aber möglicherweise angepasst werden können.

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

Update: Falls jemand auf dasselbe Problem setCrossFadeEnabled(true)stößt, das ich aus irgendeinem Grund unter Android <4.3 mit einem unerwünschten White-Out-Effekt festgestellt habe, musste ich mit der oben genannten @ Roman Minenok ValueAnimator-Methode für <4.3 zu einer Volltonfarbe wechseln.

Scottyab
quelle
Genau das habe ich gesucht. Vielen Dank! :)
Cyph3r
7

Die Antwort wird auf viele Arten gegeben. Sie können auch ofArgb(startColor,endColor)von verwendenValueAnimator .

für 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
quelle
7

Hier ist eine nette Funktion, die dies ermöglicht:

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

Und in 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()
    }
}
Android-Entwickler
quelle
Dies unterstützt keine Telefone der unteren
Preisklasse
2
@Kartheeks meinst du Android 10 und darunter? In diesem Fall können Sie problemlos eine "NineOldAndroids" -Bibliothek verwenden, um dies zu unterstützen: Nineoldandroids.com . Die Syntax ist fast genau die gleiche.
Android-Entwickler
für niedrigere Version colorAnimation.addUpdateListener (neuer ValueAnimator.AnimatorUpdateListener () {@Override public void onAnimationUpdate (ValueAnimator-Animation) {colorDrawable.setColor ((Integer) animation.getAnimatedValue ()); ViewCompat.setBackground; ;;
Mahbubur Rahman Khan
6

Die Dokumentation zu XML-basierten Animationen ist schrecklich. Ich habe gerade um Stunden durchsucht die Hintergrundfarbe einer Schaltfläche zu animieren , wenn drücken ... Das Traurige daran ist , dass die Animation weg nur ein Attribut ist: Sie können exitFadeDurationin der 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>

Dann verwenden Sie es einfach wie backgroundfür Ihre Ansicht. Kein Java / Kotlin-Code erforderlich.

Marius
quelle
Du hast meinen Tag gerettet. Vielen Dank.
Federico Heiland
1
Hervorragende Antwort. Beachten Sie, dass Sie möglicherweise auch benötigen android:enterFadeDuration; Meine Erfahrung war, dass meine Animation ohne sie das zweite Mal nicht funktionierte.
String
5

einen Ordner hinzufügen Animator in res Ordner. (Der Name muss Animator sein ). Fügen Sie eine Animator-Ressourcendatei hinzu. Zum Beispiel 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>

Rufen Sie dies in der Java-Aktivitätsdatei auf

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

Verwenden Sie die folgende Funktion für 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()
}

Übergeben Sie die Ansicht, deren Farbe Sie ändern möchten.

Günstling
quelle
2

Ich habe festgestellt, dass die Implementierung ArgbEvaluatorim Android-Quellcode beim Farbwechsel am besten funktioniert. Bei der Verwendung von HSV sprang der Übergang je nach den beiden Farben durch zu viele Farbtöne für mich. Aber diese Methode funktioniert nicht.

Wenn Sie einfach animieren versuchen, den Einsatz ArgbEvaluatormit ValueAnimatorwie vorgeschlagen hier :

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

Wenn Sie jedoch wie ich sind und Ihren Übergang mit einer Benutzergeste oder einem anderen Wert verknüpfen möchten, der von einer Eingabe übergeben wird, ValueAnimatorist dies keine große Hilfe (es sei denn, Sie zielen auf API 22 und höher ab. In diesem Fall können Sie die ValueAnimator.setCurrentFraction()Methode verwenden ). Wenn Sie auf API 22 abzielen, schließen Sie den im Quellcode gefundenen ArgbEvaluatorCode in Ihre eigene Methode ein, wie unten gezeigt:

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

Und benutze es wie du willst.

fahmy
quelle
0

Basierend auf der Antwort von ademar111190 habe ich diese Methode erstellt, die die Hintergrundfarbe einer Ansicht zwischen zwei beliebigen Farben pulsiert :

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