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.java→Demo
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多行,比较占空间,有兴趣的大佬可以去了解一下,如果使用上有问题欢迎到我GitHub的Android开发人员不得不收集的代码(持续更新中)提issue,好了,本工具类到此结束,为柯基砸上你们的喜欢吧(臭不要脸)。
网友评论