我赌5毛你没见过这样的SpannableString

Foreword

本文不是标题党哈,进来的肯定会有收获,啥也不说,先来个gif把5毛钱收起来再说。

anim_span.gif

其次是静态的。

span.png

不要小看哦,上面两张图的效果我只用了两个TextView,其实一个TextView也可以完成,但是考虑到动静可以分开来优化内存,所以动态图用了一个TextView,静态图用了一个TextView。好了,看也看完了,我是不赌赢了哈。

Introduce

我们先来分析静态图的,从标题SpanUtils可以看出,我已经为你们封装好了相关工具类,该工具类就可以为你快速创建出上图中的任一样式。Talk is cheap. Let me show the code for u.

tvAboutSpan.setText(new SpanUtils()
        .appendLine("SpanUtils").setBackgroundColor(Color.LTGRAY).setBold().setForegroundColor(Color.YELLOW).setAlign(Layout.Alignment.ALIGN_CENTER)
        .appendLine("前景色").setForegroundColor(Color.GREEN)
        .appendLine("背景色").setBackgroundColor(Color.LTGRAY)
        .appendLine("行高顶部对齐").setLineHeight(2 * lineHeight, SpanUtils.ALIGN_TOP).setBackgroundColor(Color.GREEN)
        .appendLine("行高居中对齐").setLineHeight(2 * lineHeight, SpanUtils.ALIGN_CENTER).setBackgroundColor(Color.LTGRAY)
        .appendLine("行高底部对齐").setLineHeight(2 * lineHeight, SpanUtils.ALIGN_BOTTOM).setBackgroundColor(Color.GREEN)
        .appendLine("测试段落缩,首行缩进两字,其他行不缩进").setLeadingMargin((int) textSize * 2, 10).setBackgroundColor(Color.GREEN)
        .appendLine("测试引用,后面的字是为了凑到两行的效果").setQuoteColor(Color.GREEN, 10, 10).setBackgroundColor(Color.LTGRAY)
        .appendLine("测试列表项,后面的字是为了凑到两行的效果").setBullet(Color.GREEN, 20, 10).setBackgroundColor(Color.LTGRAY).setBackgroundColor(Color.GREEN)
        .appendLine("测试图标文字顶部对齐,后面的字是为了凑到两行的效果").setIconMargin(R.drawable.shape_spannable_block_high, 20, SpanUtils.ALIGN_TOP).setBackgroundColor(Color.LTGRAY)
        .appendLine("测试图标文字居中对齐,后面的字是为了凑到两行的效果").setIconMargin(R.drawable.shape_spannable_block_high, 20, SpanUtils.ALIGN_CENTER).setBackgroundColor(Color.GREEN)
        .appendLine("测试图标文字底部对齐,后面的字是为了凑到两行的效果").setIconMargin(R.drawable.shape_spannable_block_high, 20, SpanUtils.ALIGN_BOTTOM).setBackgroundColor(Color.LTGRAY)
        .appendLine("测试图标顶部对齐,后面的字是为了凑到两行的效果").setIconMargin(R.drawable.shape_spannable_block_low, 20, SpanUtils.ALIGN_TOP).setBackgroundColor(Color.GREEN)
        .appendLine("测试图标居中对齐,后面的字是为了凑到两行的效果").setIconMargin(R.drawable.shape_spannable_block_low, 20, SpanUtils.ALIGN_CENTER).setBackgroundColor(Color.LTGRAY)
        .appendLine("测试图标底部对齐,后面的字是为了凑到两行的效果").setIconMargin(R.drawable.shape_spannable_block_low, 20, SpanUtils.ALIGN_BOTTOM).setBackgroundColor(Color.GREEN)
        .appendLine("32dp字体").setFontSize(32, true)
        .appendLine("2倍字体").setFontProportion(2)
        .appendLine("横向2倍字体").setFontXProportion(1.5f)
        .appendLine("删除线").setStrikethrough()
        .appendLine("下划线").setUnderline()
        .append("测试").appendLine("上标").setSuperscript()
        .append("测试").appendLine("下标").setSubscript()
        .appendLine("粗体").setBold()
        .appendLine("斜体").setItalic()
        .appendLine("粗斜体").setBoldItalic()
        .appendLine("monospace字体").setFontFamily("monospace")
        .appendLine("自定义字体").setTypeface(Typeface.createFromAsset(getAssets(), "fonts/dnmbhs.ttf"))
        .appendLine("相反对齐").setAlign(Layout.Alignment.ALIGN_OPPOSITE)
        .appendLine("居中对齐").setAlign(Layout.Alignment.ALIGN_CENTER)
        .appendLine("正常对齐").setAlign(Layout.Alignment.ALIGN_NORMAL)
        .append("测试").appendLine("点击事件").setClickSpan(clickableSpan)
        .append("测试").appendLine("Url").setUrl("https://github.com/Blankj/AndroidUtilCode")
        .append("测试").appendLine("模糊").setBlur(3, BlurMaskFilter.Blur.NORMAL)
        .appendLine("颜色渐变").setShader(new LinearGradient(0, 0,
                64 * density * 4, 0,
                getResources().getIntArray(R.array.rainbow),
                null,
                Shader.TileMode.REPEAT)).setFontSize(64, true)
        .appendLine("图片着色").setFontSize(64, true).setShader(new BitmapShader(ImageUtils.getBitmap(R.drawable.cheetah),
                Shader.TileMode.REPEAT,
                Shader.TileMode.REPEAT))
        .appendLine("阴影效果").setFontSize(64, true).setBackgroundColor(Color.BLACK).setShadow(24, 8, 8, Color.WHITE)
        .append("测试小图对齐").setBackgroundColor(Color.LTGRAY)
        .appendImage(R.drawable.shape_spannable_block_low, SpanUtils.ALIGN_TOP)
        .appendImage(R.drawable.shape_spannable_block_low, SpanUtils.ALIGN_CENTER)
        .appendImage(R.drawable.shape_spannable_block_low, SpanUtils.ALIGN_BASELINE)
        .appendImage(R.drawable.shape_spannable_block_low, SpanUtils.ALIGN_BOTTOM)
        .appendLine("end").setBackgroundColor(Color.LTGRAY)
        .append("测试大图字体顶部对齐").setBackgroundColor(Color.GREEN)
        .appendImage(R.drawable.shape_spannable_block_high, SpanUtils.ALIGN_TOP)
        .appendLine()
        .append("测试大图字体居中对齐").setBackgroundColor(Color.LTGRAY)
        .appendImage(R.drawable.shape_spannable_block_high, SpanUtils.ALIGN_CENTER)
        .appendLine()
        .append("测试大图字体底部对齐").setBackgroundColor(Color.GREEN)
        .appendImage(R.drawable.shape_spannable_block_high, SpanUtils.ALIGN_BOTTOM)
        .appendLine()
        .append("测试空格").appendSpace(30, Color.LTGRAY).appendSpace(50, Color.GREEN).appendSpace(100).appendSpace(30, Color.LTGRAY).appendSpace(50, Color.GREEN)
        .create());

可能有些小伙伴看过我曾经的一篇文章(工具类之SpannableStringUtils(相信你会爱上它)),没错,如今SpannableStringUtils已升级为SpanUtils,它拥有更多、更完善的设置,为了弥补这些曾经所没有的,柯基真是费了好大力气才实现了这些,比如设置图片的对齐方式,设置行高对齐方式,设置图标对齐方式等的设置,当然中间也有失败的,比如我做不到在原有样式基础上给局部文字加边框,这我真的没辙。如果我设计的这么多span都满足不了大佬们的需求,那大佬们可以自定义实现span,最终调用SpanUtils.setSpans(xxxSpan)即可,这样就完美解决了其狭隘性。

有小伙伴肯定好奇动图是怎么实现的,其实只需要一个属性动画的值来控制SpannableString的属性即可,献上源代码供大佬们参阅。

private void initAnimSpan() {
    mShaderWidth = 64 * density * 4;
    mShader = new LinearGradient(0, 0,
            mShaderWidth, 0,
            getResources().getIntArray(R.array.rainbow),
            null,
            Shader.TileMode.REPEAT);
    matrix = new Matrix();

    mBlurMaskFilterSpan = new BlurMaskFilterSpan(25);

    mShadowSpan = new ShadowSpan(8, 8, 8, Color.WHITE);

    mForegroundAlphaColorSpan = new ForegroundAlphaColorSpan(Color.TRANSPARENT);

    mForegroundAlphaColorSpanGroup = new ForegroundAlphaColorSpanGroup(0);

    mPrinterString = "打印动画,后面的文字是为了测试打印效果...";

    mSpanUtils = new SpanUtils()
            .appendLine("彩虹动画").setFontSize(64, true).setShader(mShader)
            .appendLine("模糊动画").setFontSize(64, true).setSpans(mBlurMaskFilterSpan)
            .appendLine("阴影动画").setFontSize(64, true).setBackgroundColor(Color.BLACK).setSpans(mShadowSpan)
            .appendLine("透明动画").setFontSize(64, true).setSpans(mForegroundAlphaColorSpan);
    for (int i = 0, len = mPrinterString.length(); i < len; ++i) {
        ForegroundAlphaColorSpan span = new ForegroundAlphaColorSpan(Color.TRANSPARENT);
        mSpanUtils.append(mPrinterString.substring(i, i + 1)).setSpans(span);
        mForegroundAlphaColorSpanGroup.addSpan(span);
    }
    animSsb = mSpanUtils.create();
}

private void startAnim() {
    valueAnimator = ValueAnimator.ofFloat(0, 1);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // shader
            matrix.reset();
            matrix.setTranslate((Float) animation.getAnimatedValue() * mShaderWidth, 0);
            mShader.setLocalMatrix(matrix);

            // blur
            mBlurMaskFilterSpan.setRadius(25 * (1.00001f - (Float) animation.getAnimatedValue()));

            // shadow
            mShadowSpan.setDx(16 * (0.5f - (Float) animation.getAnimatedValue()));
            mShadowSpan.setDy(16 * (0.5f - (Float) animation.getAnimatedValue()));

            // alpha
            mForegroundAlphaColorSpan.setAlpha((int) (255 * (Float) animation.getAnimatedValue()));

            // printer
            mForegroundAlphaColorSpanGroup.setAlpha((Float) animation.getAnimatedValue());

            // update
            tvAboutAnimRainbow.setText(animSsb);
        }
    });

    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.setDuration(600 * 3);
    valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
    valueAnimator.start();
}

有个小细节需要注意,当用到模糊相关的操作,最好关闭view的硬件加速,否则会遇到奇怪的现象哦。

相关demo的代码说得差不多了,下面我就来介绍一下SpanUtils,其使用了建造者模式,内部用的是SpannableStringBuilder来拼接多个SpannableString,每次appendXXX都会拼接上一次的SpannableString,最终create便返回其SpannableStringBuilder实例,具体API如下所示。

API

SpannableString相关→SpanUtils.javaDemo

setFlag           : 设置标识
setForegroundColor: 设置前景色
setBackgroundColor: 设置背景色
setLineHeight     : 设置行高
setQuoteColor     : 设置引用线的颜色
setLeadingMargin  : 设置缩进
setBullet         : 设置列表标记
setIconMargin     : 设置图标
setFontSize       : 设置字体尺寸
setFontProportion : 设置字体比例
setFontXProportion: 设置字体横向比例
setStrikethrough  : 设置删除线
setUnderline      : 设置下划线
setSuperscript    : 设置上标
setSubscript      : 设置下标
setBold           : 设置粗体
setItalic         : 设置斜体
setBoldItalic     : 设置粗斜体
setFontFamily     : 设置字体系列
setTypeface       : 设置字体
setAlign          : 设置对齐
setClickSpan      : 设置点击事件
setUrl            : 设置超链接
setBlur           : 设置模糊
setShader         : 设置着色器
setShadow         : 设置阴影
setSpans          : 设置样式
append            : 追加样式字符串
appendLine        : 追加一行样式字符串
appendImage       : 追加图片
appendSpace       : 追加空白
create            : 创建样式字符串

Conclusion

其源码我就不粘贴上来了,毕竟有1500多行,比较占空间,有兴趣的大佬可以去了解一下,如果使用上有问题欢迎到我GitHubAndroid开发人员不得不收集的代码(持续更新中)提issue,好了,本工具类到此结束,为柯基砸上你们的喜欢吧(臭不要脸)。

大佬们给个❤柯基就知足了



作者:Blankj
链接:https://www.jianshu.com/p/509b0d2626f4
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值