美文网首页Android日记andnroidAndroid_UI
我赌5毛你没见过这样的SpannableString

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

作者: Blankj | 来源:发表于2017-06-11 22:51 被阅读11511次

    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,好了,本工具类到此结束,为柯基砸上你们的喜欢吧(臭不要脸)。

    相关文章

      网友评论

      • Oort:大神,有个问题。那个模糊设置了好像会系统缓存样式。被模糊过的文字。在应用其他地方使用会自带模糊样式。
      • db41a0b4e64d:点击事件没有效果啊
        c3f1ada035f0:点击 事件没效果,而且设置点击事件字体的颜色自动变了
      • Speronie:大神,有个疑问,行高居中对齐对多行的文本能生效吗?为什么我试的时候,除了第一行那段文字可以正常居中以外,后面的文字(第二行开始)直接就不显示了呢?
      • pdog18:demo挂了。。。
      • 南宫轩涵:点击事件怎么去掉有点击字符的下划线
        Blankj:@南宫轩涵 图片比字大还是小
        南宫轩涵:@Blankj 我看到了,但是中间添加图片还是不能居中
        Blankj:@南宫轩涵 ClickSpan里的ds.setUnderlineText(true);去掉就行
      • 老余的故事:哎呀,好,无敌的好:relaxed:
      • dabf8e02a0f4:来给大神点赞:+1: :+1: :+1:
      • ramblejoy:66666666666
      • 柴柴777:有点意思呀
      • 小新哥的大梦想:赞一个。。。。。。
      • ed6918f57426:第五行的变量lineHeight的值是多少?在哪声明?不止这一处变量没有,还有好多,请写清楚
        Blankj:@芥末末的沫 所以我主要写的就是使用方式哈,而不太在乎一些变量的赋值,具体Demo地址我也提供了,可以点击链接查看,源码也有相应的注释哈。
        芥末末的沫:@Blankj 一个工具库的介绍还是尽量的详细一点好啊,能够有API文档的那种写法就更好,毕竟不是几行代码就能实现的简单到一看就懂的东西。
        Blankj:@清风折柳 具体去看demo,分清主次,我只把重要的放上来了,次要的放上来浪费时间么不是
      • 大脑好饿:图片支持GIF图吗?
        路人丁Coco:@Blankj 楼主 我赌5毛 可以显示的 :yum:
        Blankj:@只是个昵称 你觉得TextView可以显示gif图吗
      • a98c05e99d6a:~\(≧▽≦)/~
      • 85289ad66c32:给大佬点个赞
      • 青蛙要fly:挺不错的
        Blankj:@青蛙要fly :sunglasses:
      • 95e44acf2b6d:楼主,五毛钱给我吧。这个种我也有。
        Blankj:@森眸暖光_369e :clap: :clap: :clap:
        95e44acf2b6d:@Blankj :sunglasses: 一大早就赚了五毛钱,想想还是很开心:kissing_heart:
        Blankj:@森眸暖光_369e 你这太不厚道了:angry:
      • 月有缘_d14c:挺好的:wink:
        Blankj:@月有缘_d14c :hushed: 不是应该相当好么:wink:
      • Alex_Cin:楼主好棒
        Blankj:@Alex_Cin 毕竟周一早上都会水一水
        Alex_Cin:@Blankj 遇到秒回的大神,我的内心是激动的
        Blankj:@Alex_Cin :sunglasses: 大家一起棒棒
      • yaoTongxue:学习了,长见识了,。多谢
        Blankj:@yaoTongxue 好好掌握基础知识,如果连群问题都回答不了,那你得好好补补自己的基础
        yaoTongxue:@Blankj QQ群输入it答案怎么错误呢。。嘻嘻,刚入道的小白,关注你了。。
        Blankj:@yaoTongxue :sunglasses:
      • 楊帥:赞
      • 9e07fa19e1ed:已经爱上他了
        Blankj:@jonenet0619 :smile:
      • CooperJ:赞一个
      • Xdjm:第一

      本文标题:我赌5毛你没见过这样的SpannableString

      本文链接:https://www.haomeiwen.com/subject/yiqkqxtx.html