让你的TextView字体跳动起来

textview可以有跑马灯的效果,如何让textview字体可以像波浪形状跳动起来呢?先上个图这里写图片描述
偶尔看了一个跳动的textview的开源项目,了解其原理,得到了一个很好用的类。还是很span有关,感觉span这个类好强大。
1. 下面介绍一下原理,利用SuperscriptSpan这个类,有两个函数
updateDrawState函数,还有一个updateMeasureState函数,可以获取textview的baslineshift.这个是字体的底部的基线值。然后利用属性动画的valueAnimator动态改变baslineshift,就可以让textview的字体跳动起来。是不是很简单。

下面看一下代码:
public class JumpingSpan extends SuperscriptSpan implements ValueAnimator.AnimatorUpdateListener{
    private final WeakReference textView;
    private final int delay;//每一个字体的间隔的时间
    private final int loopDuration;//设置字体跳动的循环的时间
    private final float animatedRange;//字体摆动的速度范围0,1
    private int shift;//字体摆动的偏移量
    private ValueAnimator jumpAnimator;
    public JumpingSpan(@NonNull TextView textView,
                            @IntRange(from = 1) int loopDuration,
                            @IntRange(from = 0) int position,
                            @IntRange(from = 0) int waveCharOffset,//单个字体偏移量时间间隔
                            @FloatRange(from = 0, to = 1, fromInclusive = false) float animatedRange) {
        this.textView = new WeakReference<>(textView);
        this.delay = waveCharOffset * position;
        this.loopDuration = loopDuration;
        this.animatedRange = animatedRange;
    }


    @Override
    public void updateDrawState(TextPaint tp) {
        super.updateDrawState(tp);
        initIfNecessary(tp.ascent());
        tp.baselineShift = shift;//更新字体底部基线的偏移量

    }

    @Override
    public void updateMeasureState(TextPaint tp) {
        super.updateMeasureState(tp);
        initIfNecessary(tp.ascent());
        tp.baselineShift = shift;
    }

    private void initIfNecessary(float ascent) {
        if (jumpAnimator != null) {
            return;
        }

        this.shift = 0;
        int maxShift = (int) ascent / 2;
        //设置最大字体偏移量  maxshift*(0,1)
        jumpAnimator = ValueAnimator.ofInt(0, maxShift);
        jumpAnimator
                .setDuration(loopDuration)
                .setStartDelay(delay);
        jumpAnimator.setInterpolator(new JumpInterpolator(animatedRange));
        jumpAnimator.setRepeatCount(ValueAnimator.INFINITE);
        jumpAnimator.setRepeatMode(ValueAnimator.RESTART);
        jumpAnimator.addUpdateListener(this);
        jumpAnimator.start();
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        // No need for synchronization as this always runs on main thread anyway
        TextView v = textView.get();
        if (v != null) {
            updateAnimationFor(animation, v);
        }
    }

    private void updateAnimationFor(ValueAnimator animation, TextView v) {
        if (isAttachedToHierarchy(v)) {
            //设置最大字体偏移量  maxshift*(0,1)
            shift = (int) animation.getAnimatedValue();//
            Log.e("gac","shift:"+shift);
            v.invalidate();
        }
    }

    private static boolean isAttachedToHierarchy(View v) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            return v.isAttachedToWindow();
        }
        return v.getParent() != null;   // Best-effort fallback (without adding support-v4 just for this...)
    }

    private static class JumpInterpolator implements TimeInterpolator {

        private final float animRange;

        public JumpInterpolator(float animatedRange) {
            animRange = Math.abs(animatedRange);//字体摆动的速度 0-1
            Log.e("gac","animRange:"+animatedRange);
        }

        @Override
        public float getInterpolation(float input) {
            // We want to map the [0, PI] sine range onto [0, animRange]
            if (input > animRange) {
                return 0f;
            }//(0,animRange)//在这个速度范围内摆动的弧度(0,pi)
            double radians = (input / animRange) * Math.PI;
            Log.e("gac","radians:"+ Math.sin(radians));
            return (float) Math.sin(radians);//(0,1)
        }

    }
}

如何利用上面的类实现效果呢?


 tv = (TextView) findViewById(R.id.tv);
        SpannableStringBuilder sbb = new SpannableStringBuilder(tv.getText());
        //buildSingleSpan(sbb);
        buildWavingSpans(sbb,tv);
        tv.setText(sbb);


 private JumpingSpan[] buildWavingSpans(SpannableStringBuilder sbb,TextView tv) {
        JumpingSpan[] spans;
        int loopDuration = 1300;
        int startPos = 0;//textview字体的开始位置
        int endPos = tv.getText().length();//结束位置 
        int waveCharDelay = loopDuration / (3 * (endPos - startPos));//每个字体延迟的时间


        spans = new JumpingSpan[endPos - startPos];
        for (int pos = startPos; pos < endPos; pos++) {//设置每个字体的jumpingspan
            JumpingSpan jumpingBean =
                    new JumpingSpan(tv, loopDuration, pos - startPos, waveCharDelay, 0.65f);
            sbb.setSpan(jumpingBean, pos, pos + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            spans[pos - startPos] = jumpingBean;
        }
        return spans;
    }

当然了这个不是我想出来的,原项目在这里https://github.com/frakbot/JumpingBeans,我只是稍微理解一下,不能光会用,需要知道人家大概是怎么实现的,万一发现好玩的有用的东西呢?

你可能感兴趣的:(android)