Анимировать изменение цвета фона просмотра на Android

335

Как вы анимируете изменение цвета фона представления на Android?

Например:

У меня есть вид с красным цветом фона. Цвет фона представления меняется на синий. Как я могу сделать плавный переход между цветами?

Если это невозможно сделать с помощью представлений, альтернатива будет приветствоваться.

hpique
источник
11
Можно было бы ожидать, что плавный переход между цветами не должен быть проблемой для платформы, такой как Android. Может быть, представления не созданы для этого, хотя. Вопрос все еще стоит.
hpique
1
Хороший видеоурок
Алекс Джолиг

Ответы:

374

В итоге я нашел (довольно хорошее) решение этой проблемы!

Вы можете использовать TransitionDrawable для достижения этой цели. Например, в файле XML в папке drawable вы можете написать что-то вроде:

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

Затем в вашем XML для фактического представления вы бы ссылались на этот TransitionDrawable в android:backgroundатрибуте.

На этом этапе вы можете инициировать переход в своем коде по команде, выполнив:

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

Или запустите переход в обратном порядке, вызвав:

transition.reverseTransition(transitionTime);

См . Ответ Романа о другом решении, использующем API-интерфейс анимации свойств, который не был доступен на момент публикации этого ответа.

боготворить
источник
3
Спасибо mxrider! Это действительно отвечает на вопрос. К сожалению, это не работает для меня, потому что представление, которое имеет TransitionDrawable в качестве фона, также должно быть анимированным, и кажется, что анимация представления переопределяет переход фона. Любые идеи?
hpique
6
Решил это, запустив TransitionDrawable ПОСЛЕ запуска анимации.
hpique
2
Потрясающие! Рад, что ты получил это работает! Есть много интересных вещей, которые вы можете сделать в xml с Android - большая часть которых вроде как спрятана в документах и, на мой взгляд, неочевидна.
боготворение
4
Совершенно не очевидно. Я должен отметить, что я не использую xml в моем случае, поскольку цвет является динамическим. Ваше решение также работает, если вы создаете TransitionDrawable по коду.
hpique
2
Одним из главных недостатков является отсутствие полноценного слушателя анимации
Лев Droidcoder
511

Вы можете использовать новый Property Animation Api для цветной анимации:

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

Для обратной совместимости с Android 2.x используйте библиотеку Nine Old Androids от Jake Wharton.

Этот getColorметод устарел в Android M, поэтому у вас есть два варианта:

  • Если вы используете библиотеку поддержки, вам необходимо заменить getColorвызовы на:

    ContextCompat.getColor(this, R.color.red);
  • если вы не используете библиотеку поддержки, вам необходимо заменить getColorвызовы на:

    getColor(R.color.red);
Роман Миненок
источник
2
"musthave" решение, если вы используете Property Animation API (или NineOldAndroid). Анимирует очень плавно.
Дмитрий Зайцев
1
Есть ли способ установить продолжительность перехода таким образом?
iGio90
159
@ iGio90 да, ты не поверишь, но вызов метода setDuration () :)
Роман Миненок
6
ValueAnimator.ofArgb(colorFrom, colorTo)выглядит более точно, но, к сожалению, это новое.
TWiStErRob
1
Прекрасное решение! Спасибо.
Стив Фолли
137

В зависимости от того, как ваш вид получает цвет фона и как вы получаете целевой цвет, есть несколько разных способов сделать это.

Первые два используют фреймворк Android Property Animation .

Используйте Аниматор Объектов, если:

  • Ваш вид имеет фоновый цвет, определенный как argbзначение в XML-файле.
  • Ваш вид ранее имел свой цвет, установленный view.setBackgroundColor()
  • Ваш вид имеет цвет фона, определенный в рисовании, который НЕ определяет никаких дополнительных свойств, таких как радиус обводки или угла.
  • Ваш вид имеет цвет фона, заданный в режиме рисования, и вы хотите удалить любые дополнительные свойства, такие как обводки или угловые радиусы, имейте в виду, что удаление дополнительных свойств не будет анимированным.

Аниматор объекта работает путем вызова, view.setBackgroundColorкоторый заменяет определенный объект рисования, если только он не является экземпляром a ColorDrawable, которым он редко является. Это означает, что любые дополнительные свойства фона из рисованного объекта, такого как обводка или углы, будут удалены.

Используйте Value Animator, если:

  • Ваш вид имеет цвет фона, заданный в чертеже, который также устанавливает свойства, такие как обводка или угловые радиусы, и вы хотите изменить его на новый цвет, который определяется во время работы.

Используйте рисование перехода, если:

  • Ваше представление должно переключаться между двумя объектами рисования, которые были определены до развертывания.

У меня были некоторые проблемы с производительностью отрисовок Transition, которые запускаются при открытии DrawerLayout, которые я не смог решить, поэтому, если вы столкнетесь с неожиданным заиканием, вы можете столкнуться с той же ошибкой, что и я.

Вам нужно будет изменить пример Value Animator, если вы хотите использовать рисование StateLists или LayerLists , в противном случае он потерпит крах на final GradientDrawable background = (GradientDrawable) view.getBackground();линии.

Аниматор объекта :

Посмотреть определение:

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

Создайте и используйте ObjectAnimatorкак это.

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

Вы также можете загрузить определение анимации из xml, используя AnimatorInflater, как это делает XMight в Android objectAnimator animate backgroundColor of Layout

Значение аниматора :

Посмотреть определение:

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

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>

Создайте и используйте ValueAnimator следующим образом:

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

Переходный чертеж :

Посмотреть определение:

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

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>

Используйте TransitionDrawable следующим образом:

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

Вы можете отменить анимацию, вызвав .reverse()экземпляр анимации.

Есть несколько других способов сделать анимацию, но эти три, вероятно, наиболее распространенные. Я обычно использую ValueAnimator.

Маттиас
источник
на самом деле можно определить и использовать TransitionDrawable в коде, но по какой-то причине он не работает во второй раз. странно.
Android-разработчик
Я могу использовать аниматор объектов, даже если в моем представлении установлен ColorDrawable. Я просто сохраняю его в локальной переменной, устанавливаю его на ноль, а когда анимация завершается, возвращаю исходный ColorDrawable.
Милош Черниловский
55

Вы можете сделать объект аниматором. Например, у меня есть targetView, и я хочу изменить цвет фона:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();
ademar111190
источник
2
Из API 21 вы можете использовать ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Его реализация просто ofInt(...).setEvaluator(new ArgbEvaluator()).
ypresto
20

Если вы хотите цветную анимацию, как это,

Введите описание изображения здесь

этот код поможет вам:

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
источник
9
Где находится часть, когда инициализируется переменная anim ?
VoW
@VoW, это просто экземпляр ValueAnimator.
РБК
Это должно быть anim.setRepeatCount(ValueAnimator.INFINITE);не Animation.INFINITE, пока это то же самое, но у нас нет никаких гарантий
MikhailKrishtop
@MikhailKrishtop что не так с Animation.INFINITE? developer.android.com/reference/android/view/animation/…
РБК
14

Лучший способ заключается в использовании ValueAnimator иColorUtils.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

Еще один простой способ добиться этого - выполнить затухание с помощью AlphaAnimation.

  1. Сделайте ваш вид ViewGroup
  2. Добавьте к нему дочернее представление с индексом 0 с размерами компоновки match_parent
  3. Дайте вашему ребенку тот же фон, что и контейнер
  4. Изменить цвет фона контейнера на целевой цвет
  5. Исчезните ребенка с помощью AlphaAnimation.
  6. Удалить дочерний элемент, когда анимация завершена (с помощью AnimationListener)
Стефан ЯИС
источник
9

Этот метод я использую в Базовом занятии для изменения фона. Я использую GradientDrawables, сгенерированные в коде, но могут быть адаптированы для соответствия.

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

обновление: в случае, если кто-то сталкивается с той же проблемой, которую я обнаружил, по какой-то причине на Android <4.3 использование setCrossFadeEnabled(true)вызывает нежелательный эффект затемнения, поэтому мне пришлось переключиться на сплошной цвет для <4.3, используя метод @Roman Minenok ValueAnimator, упомянутый выше.

scottyab
источник
Это именно то, что я искал. Спасибо! :)
cyph3r
7

Ответ дается во многих отношениях. Вы можете также использовать ofArgb(startColor,endColor)в ValueAnimator.

для 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

Документация по XML-анимации ужасна. Я искал около часа, чтобы анимировать цвет фона кнопки при нажатии ... Печально то, что анимация только один атрибут: вы можете использовать exitFadeDurationв 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>

Тогда просто используйте это как backgroundдля вашего взгляда. Код Java / Kotlin не требуется.

Marius
источник
Ты спас мой день. Спасибо.
Федерико
1
Отличный ответ. Обратите внимание, что вам может понадобиться android:enterFadeDuration; По моему опыту, без этого моя анимация не работала во второй раз.
Строка
7

Вот хорошая функция, которая позволяет это:

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

А в Котлине

@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
источник
это не поддерживает телефоны более низкой версии
Kartheek s
2
@ Karteeks ты имеешь в виду Android 10 и ниже? если это так, вы можете легко использовать библиотеку «nineOldAndroids» для ее поддержки: nineoldandroids.com . синтаксис почти точно такой же.
Android-разработчик
для более низкой версии colorAnimation.addUpdateListener (new ValueAnimator.AnimatorUpdateListener () {@Override public void onAnimationUpdate (ValueAnimator animation) {colorDrawable.setColor ((Integer) animation.getAnimatedValue () ()) colorBoBat). View) ;
Махбубур Рахман Хан
5

добавить папку аниматор в папку res . (имя должно быть аниматором ). Добавьте файл ресурсов аниматора. Например, 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>

Внутри Java-файла Activity, позвоните

View v = getWindow().getDecorView().findViewById(android.R.id.content);
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.fade);
set.setTarget(v);
set.start();
canbax
источник
3

Используйте функцию ниже для 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()
}

Передайте любой вид, который вы хотите изменить цвет.

миньон
источник
2

Я обнаружил, что реализация, используемая ArgbEvaluator в исходном коде Android, лучше всего справляется с переходом цветов. При использовании HSV, в зависимости от двух цветов, переход прыгал через слишком много оттенков для меня. Но этот метод не.

Если вы пытаетесь просто анимировать, используйте ArgbEvaluatorс, ValueAnimatorкак предлагается здесь :

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

Однако, если вы похожи на меня и хотите связать свой переход с каким-либо пользовательским жестом или другим значением, переданным из входных данных, ValueAnimatorэто не очень поможет (если только вы не ориентируетесь на API 22 и выше, в этом случае вы можете использовать ValueAnimator.setCurrentFraction()метод ). При таргетинге ниже API 22 оберните код, найденный в ArgbEvaluatorисходном коде, в свой собственный метод, как показано ниже:

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

И используйте его как хотите.

Фахми
источник
0

Основываясь на ответе ademar111190 , я создал этот метод, который будет изменять цвет фона представления между любыми двумя цветами:

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();
}
запрет-геоинженерия
источник