文本颜色动画

18
有没有一种方法可以使文本颜色动画变化(从任何颜色到白色)?
我想到的唯一变通方法是在同一个位置放置两个文本视图(具有相同的文本),并淡化顶部的一个,这样底部的那个(具有白色)就会变得可见。
附注:我放弃了使用两个TextView的方法,因为它看起来很奇怪(边缘不平滑,并且由于我屏幕上有很多这样的元素,所以滚动非常卡顿)。我做的是一个疯狂的hack,它使用Thread和setTextColor进行动画处理(这也会强制重绘一个TextView)。
由于我只需要两种颜色变化(从红色到白色,从绿色到白色),我硬编码了值和它们之间所有的过渡颜色。所以它的外观如下:
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;
    }
}
}
8个回答

56
你可以使用新的属性动画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 向后兼容,请使用 Jake Wharton 的 Nine Old Androids 库


44

最简单的解决方案是使用对象动画器:

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

如何控制ObjectAnimator的速度? - pollaris
@pollaris 设置了动画时长 colorAnim.setDuration(1000); - MarceloBarbosa
完美的解决方案,我用它来制作漂亮的动画点击效果,并在文本视图上实现柔和的淡出效果,因为使用颜色选择器的默认行为太快并且看起来很丑。 - Luigi_Papardelle

3

无需保留两个文本视图的句柄。首先添加淡入/淡出动画:

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

我就像这样实现了它,所以证明它有效。

3

最佳方式使用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();

1
正如其他人提到的那样,使用 ObjectAnimator 可以解决这个问题。然而,在现有的帖子中 - 我没有看到如何设置持续时间。对我来说,颜色变化会立即发生。
以下解决方案显示:
1. 使用一些间隔设置动画; 感谢该贴:https://plus.google.com/+CyrilMottier/posts/X4yoNHHszwq 2. 一种在两种颜色之间不断循环的方法
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();
}

1

虽然我还没有找到完全不同的方法,但我尝试使用TextSwitcher(带有淡入淡出动画)来创建颜色变化效果。 TextSwitcher是一种ViewSwitcher,它在两个(内部)TextView之间进行动画处理。你是否无意中手动实现了相同的系统? ;) 它可以更好地管理一些过程,因此您可能会发现它更容易使用(特别是如果您想尝试更复杂的动画)。我将创建TextSwitcher的新子类和一些方法,例如setColour(),它可以设置新颜色,然后触发动画。然后可以将动画代码移动到主应用程序之外。

  • 确保您保留了放入切换器中的两个TextView的句柄
  • 更改另一个TextView的颜色并调用setText()以在它们之间进行动画处理

如果您已经使用ViewSwitcher,那么我认为没有更简单的实现方法。


谢谢。实际上我还没有实现我所说的方法(只是想到了它)。非常感谢,我现在会尝试你的方法 :) - Alex Orlov

0

我删除了两个TextView的变量,因为它看起来很奇怪(边缘不平滑,并且由于屏幕上有很多这样的元素,滚动非常缓慢)。我使用Thread和setTextColor进行了一个疯狂的hack,它还强制重绘textview的动画。

由于我只需要两种颜色变化(从红色到白色,从绿色到白色),我硬编码了所有过渡颜色的值。所以这是它的样子:

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

0
我发现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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接