一句话概括,平移再画一次。
Textview 默认就支持跑马灯效果,但需要设置一些东西,满足一些条件。
但我想知道它的原理。
看这效果,就是平滑移动 Textview 的内容,这不就是 mScrollX 干的吗,结合 ValueAnimator 很容易就实现了。
难点在于,把文字的头部重新从右边开始画出来。
width 代表 Textview 的实际宽度。
lineWidth 代表文字宽度,比如 “ABCD” 的宽度。
gap 代表文字头部和尾部的间隙,默认为 width/3。
mGhostStart ,幽灵的起始位置?
当 Textview 平移了 mGhostStart 后,正好展示完 gap 的最后一像素。
在下一帧,就需要重新绘制文字头部了。
canvas.translate(mMaxScroll, 0.0f); getLayout().draw(canvas, null, null, 0);
canvas 平移这段距离,就像是重新又从 A 开始绘画了。
知道原理后,用什么类实现都可以,改变 gap,改变速度,改变方向,垂直跑马灯等等。
canvas 真的是爹。
代码:
public class MyTextView extends TextView { public MyTextView(Context context) { super(context); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); } public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } // @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); if (isRunning() && shouldDrawGhost()) { // 平移重画 canvas.translate(mMaxScroll, 0.0f); getLayout().draw(canvas, null, null, 0); } canvas.restore(); } // 跑马中 boolean running = false; public boolean isRunning() { return running; } // 该画幽灵部分了 boolean shouldDrawGhost() { return isRunning() && getScrollX() > mGhostStart; } private float mMaxScroll; private float mGhostStart; public void start() { running = true; final int textWidth = getWidth(); final float lineWidth = getLayout().getLineWidth(0); final float gap = textWidth / 3.0f; mGhostStart = lineWidth - textWidth + gap; mMaxScroll = lineWidth + gap; ValueAnimator animator = ValueAnimator.ofFloat(0, mMaxScroll); animator.setDuration(5000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (Float) animation.getAnimatedValue(); setScrollX((int) f); if (1 == animation.getAnimatedFraction()) { running = false; } } }); animator.start(); } }