Текстовая анимация цвета

Есть ли способ анимировать изменение цвета текста (от anycolor до белого)?

Единственный вариант, с которым я столкнулся, помещает два текстовых изображения (с одним и тем же текстом) в одном месте и затухает над верхней, поэтому нижний (имеющий белый цвет) станет видимым.

P.S. Я отказался от варианта 2 TextViews, так как он выглядел странно (края были негладкими, и, поскольку на экране было много таких элементов, это действительно задерживало прокрутку). То, что я сделал, было сумасшедшим взломом, который делает анимацию с использованием Thread и setTextColor (что также заставляет перерисовать текстовое изображение).

Поскольку мне нужно было всего 2 изменения цвета (от красного до белого и от зеленого до белого), я жестко закодировал значения и все цвета перехода между ними. Итак, вот как это выглядит:

public class BlinkingTextView extends TextView {
public BlinkingTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public void animateBlink(final boolean red) {
    if (animator != null) {
        animator.drop();
    }
    animator = new Animator(this, red);
    animator.start();
}

public void clearBlinkAnimation() {
    if (animator != null) {
        animator.drop();
    }
}

private Animator animator;

private final static class Animator extends Thread {
    public Animator(final TextView textView, final boolean red) {
        this.textView = textView;
        if (red) {
            SET_TO_USE = RED;
        } else {
            SET_TO_USE = GREEN;
        }
    }

    private TextView textView;

    private final int[] SET_TO_USE;

    private final static int[] RED = {
        -2142396,
        -2008754,
        -1874854,
        -1740697,
        -1540490,
        -1405563,
        -1205099,
        -1004634,
        -804170,
        -669243,
        -469036,
        -334879,
        -200979,
        -67337,
        -1
    };
    private final static int[] GREEN = {
        -6959821,
        -6565826,
        -6106293,
        -5646758,
        -5055894,
        -4530309,
        -3939444,
        -3283042,
        -2692177,
        -2166592,
        -1575728,
        -1116193,
        -656660,
        -262665,
        -1
    };

    private boolean stop;

    @Override
    public void run() {
        int i = 0;
        while (i < 15) {
            if (stop) break;
            final int color = SET_TO_USE[i];
            if (stop) break;
            textView.post(new Runnable() {
                @Override
                public void run() {
                    if (!stop) {
                        textView.setTextColor(color);                       
                    }
                }
            });
            if (stop) break;
            i++;
            if (stop) break;
            try {
                Thread.sleep(66);
            } catch (InterruptedException e) {}
            if (stop) break;
        }
    }

    public void drop() {
        stop = true;
    }
}
}

Ответ 1

Хотя я не нашел полностью отличный метод, я попытался использовать TextSwitcher (с анимацией затухания), чтобы создать эффект изменения цвета. A TextSwitcher - это своего рода ViewSwitcher, который буквально оживляет между двумя (внутренними) TextView s. Вы вручную использовали ту же систему бессознательно?;) Он управляет немного больше процесса для вас, поэтому вам будет легче работать (особенно если вы хотите попробовать более привлекательные анимации). Я бы создал новый подкласс TextSwitcher и некоторые методы, например. setColour(), который может установить новый цвет, а затем вызвать анимацию. Затем код анимации можно перемещать за пределы основного приложения.

  • убедитесь, что вы держите дескриптор двух TextView, которые помещаются в коммутатор
  • измените цвет другого TextView и вызовите setText() для анимации между ними

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

Ответ 2

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

Integer colorFrom = getResources().getColor(R.color.red);
Integer colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animator) {
        textView.setTextColor((Integer)animator.getAnimatedValue());
    }

});
colorAnimation.start();

Для обратной совместимости с Android 2.x используйте девятую библиотеку старых андроидов от Джейка Уортона.

Ответ 3

Самое простое решение - использовать объектные аниматоры:

ObjectAnimator colorAnim = ObjectAnimator.ofInt(yourTextView, "textColor",
            Color.RED, Color.Green);
            colorAnim.setEvaluator(new ArgbEvaluator());
            colorAnim.start();

Ответ 4

Не нужно сохранять дескрипторы двух видов текста. Сначала добавьте анимацию fadeIn/fadeOut:

textSwitcher.setInAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_in));
textSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));

то

TextView currentTextView = (TextView)(textSwitcher.getNextView().equals(
  textSwitcher.getChildAt(0)) ? 
  textSwitcher.getChildAt(1) : textSwitcher.getChildAt(0)
);
// setCurrentText() first to be the same as newText if you need to
textSwitcher.setTextColor(fadeOutColor);
((TextView) textSwitcher.getNextView()).setTextColor(Color.WHITE);
textSwitcher.setText(newText);

Просто реализовано так, как это было доказано, чтобы работать.

Ответ 5

лучший способ использовать 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();

              textView.setTextColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF")
                                    , Color.parseColor("#000000")
                                    , fractionAnim));
        }
});
valueAnimator.start();

Ответ 6

Я отказался от варианта 2 TextViews, так как он выглядел странно (края были негладкими, и, поскольку на экране было много таких элементов, это действительно зависело от прокрутки). То, что я сделал, было сумасшедшим взломом, который делает анимацию с использованием Thread и setTextColor (что также заставляет перерисовать текстовое изображение).

Поскольку мне нужно было всего 2 изменения цвета (от красного до белого и от зеленого до белого), я жестко закодировал значения и все цвета перехода между ними. Итак, вот как это выглядит:

public class BlinkingTextView extends TextView {
public BlinkingTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public void animateBlink(final boolean red) {
    if (animator != null) {
        animator.drop();
    }
    animator = new Animator(this, red);
    animator.start();
}

public void clearBlinkAnimation() {
    if (animator != null) {
        animator.drop();
    }
}

private Animator animator;

private final static class Animator extends Thread {
    public Animator(final TextView textView, final boolean red) {
        this.textView = textView;
        if (red) {
            SET_TO_USE = RED;
        } else {
            SET_TO_USE = GREEN;
        }
    }

    private TextView textView;

    private final int[] SET_TO_USE;

    private final static int[] RED = {
        -2142396,
        -2008754,
        -1874854,
        -1740697,
        -1540490,
        -1405563,
        -1205099,
        -1004634,
        -804170,
        -669243,
        -469036,
        -334879,
        -200979,
        -67337,
        -1
    };
    private final static int[] GREEN = {
        -6959821,
        -6565826,
        -6106293,
        -5646758,
        -5055894,
        -4530309,
        -3939444,
        -3283042,
        -2692177,
        -2166592,
        -1575728,
        -1116193,
        -656660,
        -262665,
        -1
    };

    private boolean stop;

    @Override
    public void run() {
        int i = 0;
        while (i < 15) {
            if (stop) break;
            final int color = SET_TO_USE[i];
            if (stop) break;
            textView.post(new Runnable() {
                @Override
                public void run() {
                    if (!stop) {
                        textView.setTextColor(color);                       
                    }
                }
            });
            if (stop) break;
            i++;
            if (stop) break;
            try {
                Thread.sleep(66);
            } catch (InterruptedException e) {}
            if (stop) break;
        }
    }

    public void drop() {
        stop = true;
    }
}
}

Ответ 7

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

public static void changeTextColor(final TextView textView, int startColor, int endColor,
                                   final long animDuration, final long animUnit){
    if (textView == null) return;

    final int startRed = Color.red(startColor);
    final int startBlue = Color.blue(startColor);
    final int startGreen = Color.green(startColor);

    final int endRed = Color.red(endColor);
    final int endBlue = Color.blue(endColor);
    final int endGreen = Color.green(endColor);

    new CountDownTimer(animDuration, animUnit){
        //animDuration is the time in ms over which to run the animation
        //animUnit is the time unit in ms, update color after each animUnit

        @Override
        public void onTick(long l) {
            int red = (int) (endRed + (l * (startRed - endRed) / animDuration));
            int blue = (int) (endBlue + (l * (startBlue - endBlue) / animDuration));
            int green = (int) (endGreen + (l * (startGreen - endGreen) / animDuration));

            textView.setTextColor(Color.rgb(red, green, blue));
        }

        @Override
        public void onFinish() {
            textView.setTextColor(Color.rgb(endRed, endGreen, endBlue));
        }
    }.start();
}

Ответ 8

Как упоминают другие, использование ObjectAnimator решает для этого. Однако в существующих сообщениях я не видел, как установить продолжительность. Для меня изменение цвета произойдет немедленно.

В приведенном ниже решении показано:

  • настройка анимации с некоторым интервалом; благодаря сообщению: https://plus.google.com/+CyrilMottier/posts/X4yoNHHszwq

  • способ непрерывного циклического переключения между двумя цветами


void animateTextViewColors(TextView textView, Integer colorTo) {

    final Property<TextView, Integer> property = new Property<TextView, Integer>(int.class, "textColor") {
        @Override
        public Integer get(TextView object) {
            return object.getCurrentTextColor();
        }

        @Override
        public void set(TextView object, Integer value) {
            object.setTextColor(value);
        }
    };

    final ObjectAnimator animator = ObjectAnimator.ofInt(textView, property, colorTo);
    animator.setDuration(8533L);
    animator.setEvaluator(new ArgbEvaluator());
    animator.setInterpolator(new DecelerateInterpolator(2));
    animator.start();
}

void oscillateDemo(final TextView textView) {

    final int whiteColor = ContextCompat.getColor(TheApp.getAppContext(), R.color.white);
    final int yellowColor = ContextCompat.getColor(TheApp.getAppContext(), R.color.yellow);

    final int counter = 100;

    Thread oscillateThread = new Thread() {
        @Override
        public void run() {

            for (int i = 0; i < counter; i++) {

                final int fadeToColor = (i % 2 == 0)
                        ? yellowColor
                        : whiteColor;

                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                        animateTextViewColors(textView, fadeToColor);
                    }
                });                                      

                try {
                    Thread.sleep(2450);
                }
                catch (InterruptedException iEx) {}
            }
        }
    };

    oscillateThread.start();
}