Animar a alteração da cor de fundo da visualização no Android

335

Como você anima a mudança da cor de fundo de uma exibição no Android?

Por exemplo:

Eu tenho uma visão com uma cor de fundo vermelho. A cor de fundo da vista muda para azul. Como posso fazer uma transição suave entre cores?

Se isso não puder ser feito com visualizações, uma alternativa será bem-vinda.

hpique
fonte
11
Seria de esperar que uma transição suave entre cores não fosse um problema para uma plataforma como o Android. Talvez as visualizações não sejam construídas para isso. A questão ainda permanece.
Hpique
11
Um bom vídeo tutorial
Alex Jolig

Respostas:

374

Acabei descobrindo uma solução (muito boa) para esse problema!

Você pode usar um TransitionDrawable para fazer isso. Por exemplo, em um arquivo XML na pasta drawable, você pode escrever algo como:

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

Em seguida, em seu XML para a visualização real, você referenciaria esse TransitionDrawable no android:backgroundatributo

Nesse ponto, você pode iniciar a transição no seu código sob comando, fazendo:

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

Ou execute a transição ao contrário, chamando:

transition.reverseTransition(transitionTime);

Consulte a resposta de Roman para outra solução usando a API de animação de propriedades, que não estava disponível no momento em que esta resposta foi postada originalmente.

idolatrar
fonte
3
Obrigado mxrider! Isso responde à pergunta. Infelizmente, isso não funciona para mim porque a exibição que tem o TransitionDrawable como plano de fundo também deve ser animada, e parece que a animação da exibição substitui a transição de segundo plano. Alguma ideia?
Hpique 9/07
6
Solucionado, iniciando o TransitionDrawable APÓS iniciar a animação.
hpique
2
Impressionante! Feliz por você fazer isso funcionar! Há muitas coisas legais que você pode fazer em xml com o Android - muitas das quais estão meio enterradas nos documentos e não são óbvias na minha opinião.
Idolize
4
Nada óbvio. Devo mencionar que não estou usando xml no meu caso, pois a cor é dinâmica. Sua solução também funciona se você criar o TransitionDrawable por código.
Hpique
2
Uma das principais desvantagens é que não há nenhuma animação ouvinte completa
Leo Droidcoder
508

Você pode usar a nova API de animação da propriedade para animação em cores:

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

Para compatibilidade com versões anteriores do Android 2.x, use a biblioteca Nine Old Androids da Jake Wharton.

O getColormétodo foi descontinuado no Android M, então você tem duas opções:

  • Se você usar a biblioteca de suporte, precisará substituir as getColorchamadas por:

    ContextCompat.getColor(this, R.color.red);
  • se você não usar a biblioteca de suporte, precisará substituir as getColorchamadas por:

    getColor(R.color.red);
Roman Minenok
fonte
2
solução "musthave" se você estiver usando a API de animação de propriedades (ou NineOldAndroid). Anima muito bem.
Dmitry Zaytsev
11
Existe alguma maneira de definir a duração da transição dessa maneira?
IGio90 22/03
159
@ iGio90 sim, você não está não vai acreditar, mas o setDuration chamada de método () :)
Roman Minenok
6
ValueAnimator.ofArgb(colorFrom, colorTo)parece mais direto ao ponto, mas infelizmente é novo.
TWiStErRob
11
Bela solução! Obrigado.
9788 Steve Folly
137

Dependendo de como sua exibição obtém a cor de fundo e de como você obtém a cor de destino, existem várias maneiras diferentes de fazer isso.

Os dois primeiros usam a estrutura Android Property Animation .

Use um Animador de Objetos se:

  • Sua visualização tem a cor de fundo definida como argb valor em um arquivo xml.
  • Sua visualização teve sua cor definida anteriormente por view.setBackgroundColor()
  • Sua visualização tem sua cor de plano de fundo definida em um drawable que NÃO define propriedades extras, como traços ou raios dos cantos.
  • Sua visualização tem a cor de fundo definida em um drawable e você deseja remover propriedades extras, como traços ou raios de canto, lembre-se de que a remoção das propriedades extras não será animada.

O animador de objeto trabalha chamando view.setBackgroundColorque substitui o drawable definido, a menos que seja uma instância de a ColorDrawable, o que raramente é. Isso significa que quaisquer propriedades extras de fundo de um drawable, como traços ou cantos, serão removidas.

Use um Value Animator se:

  • Sua visualização tem sua cor de plano de fundo definida em um drawable que também define propriedades como os traços do raio ou do canto E você deseja alterá-la para uma nova cor que é decidida durante a execução.

Use um drawable Transition se:

  • Sua visão deve alternar entre dois desenháveis ​​que foram definidos antes da implantação.

Eu tive alguns problemas de desempenho com os drawables Transition que são executados enquanto eu abro um DrawerLayout que não consegui resolver, portanto, se você encontrar alguma gagueira inesperada, poderá ter o mesmo bug que eu.

Você precisará modificar o exemplo do Value Animator se desejar usar um drawable StateLists ou um drawable LayerLists , caso contrário, ele travará na final GradientDrawable background = (GradientDrawable) view.getBackground();linha.

Animador de Objetos :

Ver definição:

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

Crie e use um ObjectAnimatorcomo este.

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

Você também pode carregar a definição de animação de um xml usando um AnimatorInflater como o XMight faz no objeto Android.

Value Animator :

Ver definição:

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

Definição Drawable:

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

Crie e use um ValueAnimator como este:

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

Drawable de transição :

Ver definição:

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

Definição Drawable:

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

Use o TransitionDrawable assim:

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

Você pode reverter as animações chamando .reverse()a instância de animação.

Existem outras maneiras de fazer animações, mas essas três são provavelmente as mais comuns. Eu geralmente uso um ValueAnimator.

Mattias
fonte
na verdade, é possível definir e usar o TransitionDrawable no código, mas por algum motivo, ele não funciona na segunda vez. esquisito.
desenvolvedor android
Sou capaz de usar o animador de objetos mesmo quando meu modo de exibição tem o ColorDrawable definido. Acabei de salvá-lo em uma variável local, defini-lo como nulo e, quando a animação terminar, defina o ColorDrawable original de volta.
Miloš Černilovský
55

Você pode criar um animador de objeto. Por exemplo, eu tenho um targetView e quero alterar sua cor de plano de fundo:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();
ademar111190
fonte
2
Na API 21, você pode usar ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Sua implementação é justa ofInt(...).setEvaluator(new ArgbEvaluator()).
Ypresto 9/08/16
20

Se você quiser animação em cores como esta,

Digite a descrição da imagem aqui

este código o ajudará:

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
fonte
9
Onde está a parte em que a variável anim é inicializada?
VoW 22/08/16
@ VOW, é apenas uma instância do ValueAnimator.
RBK
Não deve ser anim.setRepeatCount(ValueAnimator.INFINITE);Animation.INFINITE, por enquanto, é o mesmo, mas não temos garantias
MikhailKrishtop 4/18/18
@MikhailKrishtop o que há de errado com Animation.INFINITE? developer.android.com/reference/android/view/animation/…
RBK 5/18
14

A melhor maneira é usar o 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();
Rasoul Miri
fonte
12

Outra maneira fácil de conseguir isso é fazer um desbotamento usando o AlphaAnimation.

  1. Torne sua visualização um grupo de exibição
  2. Adicione uma visão filho a ela no índice 0, com dimensões de layout match_parent
  3. Dê ao seu filho o mesmo histórico do contêiner
  4. Alterar para segundo plano do contêiner para a cor de destino
  5. Desvanecer a criança usando AlphaAnimation.
  6. Remova o filho quando a animação estiver concluída (usando um AnimationListener)
Stephane JAIS
fonte
isso é um truque! bem feito . . .
Elyar abad
9

Este é o método que eu uso em uma Atividade base para mudar o plano de fundo. Estou usando o GradientDrawables gerado no código, mas pode ser adaptado para se adequar.

    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: Caso alguém encontre o mesmo problema que eu encontrei, por alguma razão no Android <4.3, o uso setCrossFadeEnabled(true)causa um efeito indesejável de apagamento, então tive que mudar para uma cor sólida para <4.3 usando o método @Roman Minenok ValueAnimator mencionado acima.

scottyab
fonte
Era exatamente isso que eu estava procurando. Obrigado! :)
cyph3r
7

A resposta é dada de várias maneiras. Você também pode usar ofArgb(startColor,endColor)de ValueAnimator.

para 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
fonte
7

Aqui está uma boa função que permite isso:

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 em 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()
    }
}
desenvolvedor android
fonte
Este suporte does not telefones versão extremidade inferior
Kartheek s
2
@Kartheeks você quer dizer Android 10 e abaixo? Nesse caso, você pode facilmente usar uma biblioteca "nineOldAndroids" para suportá-lo: nineoldandroids.com . a sintaxe é quase exatamente a mesma.
desenvolvedor android
para a versão inferior colorAnimation.addUpdateListener (novo ValueAnimator.AnimatorUpdateListener () {@Override public void onAnimationUpdate (animação ValueAnimator) {colorDrawable.setColor ((número inteiro) animation.getAnimatedValue ()); ViewCompat.setBackground (viewTo); ;
Mahbubur Rahman Khan 21/03
6

A documentação sobre animações XML é horrível. Pesquisei por horas apenas para animar a cor de fundo de um botão ao pressionar ... O triste é que a animação está a apenas um atributo de distância: Você pode usar exitFadeDurationo 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>

Em seguida, use-o como backgroundna sua visão. Não é necessário código Java / Kotlin.

Marius
fonte
Você salvou o meu dia. Obrigado.
Federico Heiland
11
Excelente resposta. Observe que você também pode precisar android:enterFadeDuration; minha experiência foi que, sem ela, minha animação não funcionou na segunda vez.
String
5

adicione um animador de pasta na pasta res . (o nome deve ser animador ). Adicione um arquivo de recurso do animador. Por exemplo, 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>

Dentro do arquivo java Activity, chame isso

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

Use a função abaixo para 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()
}

Passe na visualização que você deseja alterar a cor.

Minion
fonte
2

Eu descobri que a implementação usada por ArgbEvaluator no código-fonte do Android funciona melhor na transição de cores. Ao usar o HSV, dependendo das duas cores, a transição estava passando por muitos tons para mim. Mas esse método não.

Se você está tentando simplesmente animado, o uso ArgbEvaluatorcom ValueAnimatorcomo sugerido aqui :

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

No entanto, se você é como eu e deseja vincular sua transição a algum gesto do usuário ou outro valor passado de uma entrada, isso ValueAnimatornão ajuda muito (a menos que você esteja direcionando para a API 22 e superior, nesse caso, você pode usar o ValueAnimator.setCurrentFraction()método ) Ao segmentar abaixo da API 22, agrupe o código encontrado no ArgbEvaluatorcódigo-fonte em seu próprio método, conforme mostrado abaixo:

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 use-o como quiser.

fahmy
fonte
0

Com base na resposta de ademar111190 , eu criei esse método que pulsará a cor de fundo de uma exibição entre duas cores:

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();
}
proibição de geoengenharia
fonte