打印机效果的TextView——TyperTextView

本文介绍如何创建一个打印机效果的TextView——TyperTextView。通过模仿国外大牛的博客,作者在项目中实现了该效果。核心原理是利用MutableForegroundColorSpan设置字符样式,并通过FireworksSpanGroup管理,借助ObjectAnimator进行动画效果。文章提供了详细的源码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果

这里写图片描述

前言

  之前在一个国外大牛的博客上看到的,自己模仿实现了一下,应用在上一个项目的splash里面。

原理

  为每个字符设置样式,使用MutableForegroundColorSpan。MutableForegroundColorSpan是自定义的可设置透明度的ForegroundColorSpan。然后,利用FireworksSpanGroup类将这些MutableForegroundColorSpan管理起来,方便设置透明度。
  动效通过ObjectAnimator实现,这里相应的自定义了一个FIREWORKS_GROUP_PROGRESS_PROPERTY属性,其实没大必要。可以直接利用ValueAnimator实现动效,在updateListener里面直接调用spanGroup.setAlpha(value);。ObjectAnimator对ValueAnimator进行了封装,会调用属性的set的方法,可以看到我们在set方法里面也是调用spanGroup.setAlpha(value);。所以可以直接使用ValueAnimator,就不用自定义属性了。
  在动画过程中,从左到右去增加MutableForegroundColorSpan的alpha值。比如,如果有5个字符,动画进行到30%,那么0.3*5->1.5。此时第一个字符alpha值为1*255,完全不透明;第二个字符alpha值为0.5*255,50%透明。

源码

public class TyperTextView {

    private TextView textView;
    private ObjectAnimator objectAnimator;
    private long duration = 1000;
    private int animationTextBeg = -1;
    private int animationTextEnd = -1;

    private static final Property<FireworksSpanGroup, Float> FIREWORKS_GROUP_PROGRESS_PROPERTY =
            new Property<FireworksSpanGroup, Float>(Float.class, "FIREWORKS_GROUP_PROGRESS_PROPERTY") {

                @Override
                public void set(FireworksSpanGroup spanGroup, Float value) {
                    spanGroup.setAlpha(value);
                }

                @Override
                public Float get(FireworksSpanGroup spanGroup) {
                    return spanGroup.getAlpha();
                }
            };


    public TyperTextView(TextView textView) {
        this.textView = textView;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    public void setAnimationTextScope(int beg, int end) {
        this.animationTextBeg = beg;
        this.animationTextEnd = end;
    }

    private void setUpAnimation() {
        final FireworksSpanGroup spanGroup = new FireworksSpanGroup(0);
        //init the group with multiple spans
        //set spans on the ActionBar spannable title
        CharSequence plainString = null;
        if (textView != null) {
            plainString = textView.getText();
        }

        final SpannableString spannableString = new SpannableString(plainString);

        if (animationTextBeg == -1) {
            animationTextBeg = 0;
        }
        if (animationTextEnd == -1) {
            animationTextEnd = plainString.length();
        }

        for (int i = animationTextBeg; i < animationTextEnd; i++) {
            int color = Color.parseColor("#ffffff");
            if (textView != null) {
                color = textView.getTextColors().getDefaultColor();
            }
            MutableForegroundColorSpan span = new MutableForegroundColorSpan(0, color);
            spanGroup.addSpan(span);
            spannableString.setSpan(span, i, i + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        spanGroup.init();
        objectAnimator = ObjectAnimator.ofFloat(spanGroup, FIREWORKS_GROUP_PROGRESS_PROPERTY, 0.0f, 1.0f);
        objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if (textView != null) {
                    textView.setText(spannableString);
                }
            }
        });
        objectAnimator.setDuration(duration);
    }

    public void startAnimation() {
        setUpAnimation();
        objectAnimator.start();
    }

    public void startAnimationIndefinitely() {
        setUpAnimation();
        objectAnimator.setRepeatCount(ObjectAnimator.INFINITE);
        objectAnimator.setRepeatMode(ObjectAnimator.RESTART);
        objectAnimator.start();
    }

    public void stopAnimation() {
        objectAnimator.end();
        textView = null;
    }

    private static final class FireworksSpanGroup {
        private final float mAlpha;
        private final ArrayList<MutableForegroundColorSpan> mSpans;

        private FireworksSpanGroup(float alpha) {
            mAlpha = alpha;
            mSpans = new ArrayList<MutableForegroundColorSpan>();
        }

        public void addSpan(MutableForegroundColorSpan span) {
            span.setAlpha((int) (mAlpha * 255));
            mSpans.add(span);
        }

        public void init() {
//            Collections.shuffle(mSpans);
        }

        public void setAlpha(float alpha) {
            int size = mSpans.size();
            float total = 1.0f * size * alpha;

            for (int index = 0; index < size; index++) {
                MutableForegroundColorSpan span = mSpans.get(index);
                if (total >= 1.0f) {
                    span.setAlpha(255);
                    total -= 1.0f;
                } else {
                    span.setAlpha((int) (total * 255));
                    total = 0.0f;
                }
            }
        }



        public float getAlpha() {
            return mAlpha;
        }
    }

    private static class MutableForegroundColorSpan extends ForegroundColorSpan {

        private int mAlpha = 255;
        private int mForegroundColor;

        public MutableForegroundColorSpan(int alpha, int color) {
            super(color);
            mAlpha = alpha;
            mForegroundColor = color;
        }

        public MutableForegroundColorSpan(Parcel src) {
            super(src);
            mForegroundColor = src.readInt();
            mAlpha = src.readInt();
        }

        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(mForegroundColor);
            dest.writeFloat(mAlpha);
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            ds.setColor(getForegroundColor());
        }

        /**
         * @param alpha from 0 to 255
         */
        public void setAlpha(int alpha) {
            mAlpha = alpha;
        }

        public void setForegroundColor(int foregroundColor) {
            mForegroundColor = foregroundColor;
        }

        public float getAlpha() {
            return mAlpha;
        }

        @Override
        public int getForegroundColor() {
            return Color.argb(mAlpha, Color.red(mForegroundColor), Color.green(mForegroundColor), Color.blue(mForegroundColor));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值