使用Span实现各种酷炫效果

来源:互联网 发布:动画片制作软件 编辑:程序博客网 时间:2024/05/21 20:40

前一段时间一直在做富文本展示和文本处理,主要用到了Html.fromHtml()实现加载网页,但实现整段文本的某些特殊如个别文字的点击,改背景色、前景色等效果,就用到了我们今天要用到的Span这个类。

关于加载网页或个别文字点击效果,可以阅读我之前写的一篇文章——用TextView实现富文本展示,点击断句和语音播报。

您也关注:
- https://github.com/shuaijia/SpanString
- https://github.com/shuaijia/RichTextView
- 我的微信公众号——安卓干货营

那接下来就先看下效果图
这里写图片描述

今天会简单介绍几个Span的基本用法,也会分享一些比较酷炫的使用方法:

  • 设置字体颜色
  • 改变字体背景色
  • 给文本添加下划线
  • 给文本加边框
  • 彩虹色文字
  • 彩虹色字体渐变动画
  • 打字效果展示文本

1、设置字体颜色

这里写图片描述

/** * 设置不同颜色文字 */private void setForegroundColor() {    SpannableString spannableString = new SpannableString(                "我爱北京天安门,天安门上太阳升 我爱北京天安门,天安门上太阳升");    spannableString.setSpan(new ForegroundColorSpan(Color.RED), 0, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    tv_diff_color.setText(spannableString);}

2、改变字体背景色

这里写图片描述

/** * 设置背景色 */private void setBackgroundColor() {    SpannableString spannableString = new SpannableString(                "我爱北京天安门,天安门上太阳升 我爱北京天安门,天安门上太阳升");    spannableString.setSpan(new BackgroundColorSpan(Color.RED), 0, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    tv_bg_color.setText(spannableString);}

3、给文本添加下划线

这里写图片描述

/** * 设置超链接 */private void setLink() {    SpannableString spannableString = new SpannableString(                "我爱北京天安门,天安门上太阳升 我爱北京天安门,天安门上太阳升");    //设置下划线    spannableString.setSpan(new UnderlineSpan(), 0, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    tv_link.setText(spannableString);}

4、给文本加边框

这里写图片描述

这种效果就不再是简单的直接使用系统提供的Span类就可以了,需要我们自定义:

public class FrameSpan extends ReplacementSpan {    private final Paint mPaint;    private int mWidth;    public FrameSpan() {        mPaint = new Paint();        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(Color.RED);        mPaint.setAntiAlias(true);    }    @Override    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {        //return text with relative to the Paint        mWidth = (int) paint.measureText(text, start, end);        return mWidth;    }    @Override    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {        //draw the frame with custom Paint        canvas.drawRect(x, top, x + mWidth, bottom, mPaint);        canvas.drawText(text, start, end, x, y, paint);    }}

类似于自定义View,最重要的是在初始化画笔、获取绘制区域大小、在draw中绘制矩形边框

这里就不再重复累赘了。

然后和之前类似,使用它:

/** * 给文字加边框 */private void addBox() {    SpannableString spannableString = new SpannableString(                "我爱北京天安门,天安门上太阳升 我爱北京天安门,天安门上太阳升");    spannableString.setSpan(new FrameSpan(), 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);    tv_biankuang.setText(spannableString);}

5、彩虹色文字

这里写图片描述
自定义Span:

public class RainbowSpan extends CharacterStyle implements UpdateAppearance {    private int[] colors;    public RainbowSpan(Context context) {        colors = context.getResources().getIntArray(R.array.splash_bg);    }    @Override    public void updateDrawState(TextPaint paint) {        paint.setStyle(Paint.Style.FILL);        Shader shader = new LinearGradient(0, 0, 0, paint.getTextSize() * colors.length, colors, null,                Shader.TileMode.MIRROR);        Matrix matrix = new Matrix();        matrix.setRotate(90);        shader.setLocalMatrix(matrix);        paint.setShader(shader);    }}

其中CharSequence是一组可读的Char序列,提供了操作Char序列的接口,是所有Span类的根父类。

使用Shader进行着色渲染,LinearGradient是线性渐变,Gradient是基于Shader类,所以我们通过Paint的setShader方法来设置这个渐变.

LinearGradient参数含义:

  • X0: 渐变起初点坐标x位置
  • y0: 渐变起初点坐标y位置
  • x1: 渐变终点坐标x位置
  • y1: 渐变终点坐标y位置
  • color0: 渐变开始颜色
  • color1: 渐变结束颜色
  • tile: 平铺方式

然后使用

/** * 设置彩色字体 */private void setColofulText() {    SpannableString spannableString = new SpannableString(                "我爱北京天安门,天安门上太阳升 我爱北京天安门,天安门上太阳升");    spannableString.setSpan(new RainbowSpan(this), 0, 15, Spanned.SPAN_INCLUSIVE_INCLUSIVE);    tv_color.setText(spannableString);}

6、彩虹色文字动起来

这里写图片描述

和上一步相比,不同的是,需实时更新、重绘彩色,所以,自定义的Span类相比于上一个RainbowSpan来说,主要有以下不同:

  • 声明变量—— 横向渐变的百分比translateXPercentage,并设置对应get、set方法;
  • 在updateDrawState中使用matrix.postTranslate进行变化;
  • postTranslate是指在setRotate后平移。
public class AnimatedColorSpan extends CharacterStyle implements UpdateAppearance {    private final int[] colors;    private Shader shader = null;    private Matrix matrix = new Matrix();    private float translateXPercentage = 0;    public AnimatedColorSpan(Context context) {        colors = context.getResources().getIntArray(R.array.splash_bg);    }    public void setTranslateXPercentage(float percentage) {        translateXPercentage = percentage;    }    public float getTranslateXPercentage() {        return translateXPercentage;    }    @Override    public void updateDrawState(TextPaint paint) {        paint.setStyle(Paint.Style.FILL);        float width = paint.getTextSize() * colors.length;        if (shader == null) {            shader = new LinearGradient(0, 0, 0, width, colors, null,                    Shader.TileMode.MIRROR);        }        matrix.reset();        matrix.setRotate(90);        matrix.postTranslate(width * translateXPercentage, 0);        shader.setLocalMatrix(matrix);        paint.setShader(shader);    }}

然后使用:

/** * 设置彩色动画 */private void setColofulAnimText() {    final SpannableString spannableString = new SpannableString(                "我爱北京天安门,天安门上太阳升 我爱北京天安门,天安门上太阳升");    AnimatedColorSpan span = new AnimatedColorSpan(this);    spannableString.setSpan(span, 0, 15, 0);    // 设置动画    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(                span, ANIMATED_COLOR_SPAN_FLOAT_PROPERTY, 0, 100);    objectAnimator.setEvaluator(new FloatEvaluator());    objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            tv_color_anim.setText(spannableString);        }    });    objectAnimator.setInterpolator(new LinearInterpolator());    objectAnimator.setDuration(DateUtils.MINUTE_IN_MILLIS * 2);    objectAnimator.setRepeatCount(ValueAnimator.INFINITE);    objectAnimator.start();}

大家可能注意到了,设置动画时用到了动画属性变化器:

/** * 彩色动画 属性变化器 */private static final Property<AnimatedColorSpan, Float> ANIMATED_COLOR_SPAN_FLOAT_PROPERTY            = new Property<AnimatedColorSpan, Float>(Float.class, "ANIMATED_COLOR_SPAN_FLOAT_PROPERTY") {    @Override    public void set(AnimatedColorSpan span, Float value) {        span.setTranslateXPercentage(value);    }    @Override    public Float get(AnimatedColorSpan span) {        return span.getTranslateXPercentage();    }};

在变化器的对应方法中设置Span变化的百分比。

7、文本实现打字效果

先看看Span的写法:

这里写图片描述

public class MutableForegroundColorSpan extends CharacterStyle implements UpdateAppearance {    private int mColor = Color.BLACK;    private int mAlpha = 0 ;    @Override    public void updateDrawState(TextPaint tp) {        tp.setColor(mColor);        tp.setAlpha(mAlpha);    }    public int getColor() {        return mColor;    }    public void setColor(int color) {        this.mColor = color;    }    public void setAlpha(int alpha) {        mAlpha = alpha;    }}
public class TypeWriterSpanGroup {    private final float mAlpha;    private final ArrayList<MutableForegroundColorSpan> mSpans;    public TypeWriterSpanGroup(float alpha) {        mAlpha = alpha;        mSpans = new ArrayList<MutableForegroundColorSpan>();    }    public void addSpan(MutableForegroundColorSpan span) {        span.setAlpha((int) (mAlpha * 255));        mSpans.add(span);    }    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;    }}

思路是这样的,每打印一个文字,都是一个对应的MutableForegroundColorSpan,要想实现连续的打印每个字,我们需要创建一个集合来存放所有得Span。

循环集合中所有的Span,除了最近一个打印的字以外,其他的字设置为不透明,第一个跟随动画进行渐变。

看下动画的使用:

/** * 打字效果 */private void addTyping() {    String content = "我爱北京天安门,天安门上太阳升 我爱北京天安门,天安门上太阳升";        final SpannableString spannableString = new SpannableString(content);    // 添加Span    final TypeWriterSpanGroup group = new TypeWriterSpanGroup(0);    for (int index = 0; index <= content.length() - 1; index++) {        MutableForegroundColorSpan span = new MutableForegroundColorSpan();        group.addSpan(span);        spannableString.setSpan(span, index, index + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    }    // 添加动画    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(group, TYPE_WRITER_GROUP_ALPHA_PROPERTY, 0.0f, 1.0f);    objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            //refresh            tv_dazi.setText(spannableString);        }    });    objectAnimator.setDuration(5000);    objectAnimator.setRepeatCount(ValueAnimator.INFINITE);    objectAnimator.start();}

还有变化器:

/** * 打字动画  属性变化器 */private static final Property<TypeWriterSpanGroup, Float> TYPE_WRITER_GROUP_ALPHA_PROPERTY =            new Property<TypeWriterSpanGroup, Float>(Float.class, "TYPE_WRITER_GROUP_ALPHA_PROPERTY") {    @Override    public void set(TypeWriterSpanGroup spanGroup, Float value) {        spanGroup.setAlpha(value);    }    @Override    public Float get(TypeWriterSpanGroup spanGroup) {        return spanGroup.getAlpha();    }};

关于使用Span实现TextView的几种效果,大致就介绍到这,有错误的地方和不足的地方,希望大家提出,我们一起进步^_^。

更多精彩内容,请关注我的微信公众号——安卓干货营
这里写图片描述

阅读全文
0 0
原创粉丝点击