在上篇文章《Android Span 架构介绍》,我们讲述了Android Span的基本概念和用法,这篇文章我们就来扩展一下我们对Android Span的了解,这一定会使你感到惊奇的,惊叹Android Span竟然还能完成这些的效果,让你在Android自定义View和动画方面有更加深刻的理解,可能会帮助你你想出更加简洁的实现方式。
 本篇文章主要讲述一下两个方面的内容:

  • 自定义Android Span

  • 使用Android Span实现动画

 先贴一下本篇文章实现的自定义Span和动画的效果图

自定义Span

 我们都知道,自定义View有两种方式,一种是继承特定的视图类,比如你希望修改TextView的行为,所以继承了TextView;另一种就是直接继承View或者ViewGroup,这样可以实现全新的视图和行为。如同自定义View一样,你有两种自定义Span的方法,一种直接继承特定类型的Span类,比如ForegroundColorSpan等,这样你可以在这些类的基础上进行修改;另一种就是继承ReplaceSpan这样的抽象类或者实现LetterLineBackgroundSpan这样的接口,你只要实现它给出的接口,就可以实现新的效果。
 我们先来讲解第一种方式。直接继承现有的Span。文章开头时展示的ActionBar动画就是通过继承ForegroundSpan来实现的。
 我们主要重载了updateDrawsStategetForegroundColor,这样就可以通过改变setAlpha函数来改变颜色,让字体从透明(alpha为0)到某个特定颜色。

public class MutableForegroundColorSpan extends ForegroundColorSpan {private int mAlpha = 255;private int mForegroundColor;public MutableForegroundColorSpan(int alpha,int color) {super(color);mAlpha = alpha;mForegroundColor = color;}@Overridepublic void updateDrawState(TextPaint ds) {ds.setColor(getForegroundColor());}public void setAlpha(int alpha) {mAlpha = alpha;}public void setForegroundColor(int foregroundColor) {mForegroundColor = foregroundColor;}public float getAlpha() {return mAlpha;}@Overridepublic int getForegroundColor() {return Color.argb(mAlpha,Color.red(mForegroundColor),Color.green(mForegroundColor),Color.blue(mForegroundColor));}
}

 第二种方法是继承Span架构中的抽象类或者是实现特定接口。需要注意的是,在上一篇文章中说的CharacterStyle,ParagraphStyle.UpdateAppearanceUpdateLayout都是没有函数的,所以,我们无法直接继承或者实现它们。除了第一篇文章中所介绍的那些Span可以使用第一种方法进行继承。我们一般都继承或者实现MetricAffectingSpan,ReplacementSpan或者LineBackgroundSpan
 比如我们想给每个字都添加一个不同颜色的背景,我们就可以继承ReplacementSpan

public class BubbleSpan extends ReplacementSpan {private Paint mPaint;static Random random = new Random();private int mWidth = -1;private RectF mRectF = new RectF();private int[] mColors = new int[20];public BubbleSpan() {initPaint();initColors();}private void initPaint() {mPaint = new Paint();mPaint.setColor(Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255)));mPaint.setAntiAlias(true);}private void initColors() {for(int index = 0 ; index < mColors.length ; index++) {mColors[index] = Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255));}}@Overridepublic int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {//return text with relative to the PaintmWidth = (int) paint.measureText(text, start, end);return mWidth;}@Overridepublic void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {float charx = x;for(int i = start ; i<end; i++) {String charAt = extractText(text, i, i + 1);float charWidth = paint.measureText(charAt);mRectF.set(charx, top, charx += charWidth, bottom);mPaint.setColor(mColors[i % mColors.length]);//根据每个字的位置绘制背景canvas.drawOval(mRectF, mPaint);}//绘制字体,如果不掉用这个函数,就不会显示字体啦。canvas.drawText(text, start, end, x, y, paint);}private String extractText(CharSequence text, int start, int end) {return text.subSequence(start, end).toString();}
}

 我们可以看到,我们要实现两个函数:getSizedrawgetSize是获得字体的长度的,所以一般都是直接使用paint.measureText,然后draw中进行绘制,你可以在这里把每个字的背景绘制出来,而且你必须也要把字体给绘制出来。如果你只想绘制背景,不想涉及字体的绘制,那么就可以直接实现LineBackgroundSpan接口。

public class RectSpan extends ReplacementSpan {private final Paint mPaint;private int mWidth;public RectSpan() {mPaint = new Paint();mPaint.setStyle(Paint.Style.STROKE);mPaint.setColor(Color.BLUE);mPaint.setAntiAlias(true);}@Overridepublic int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {//return text with relative to the PaintmWidth = (int) paint.measureText(text, start, end);return mWidth;}@Overridepublic void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {//只绘制了外围矩形,没有绘制文字。canvas.drawRect(x, top, x + mWidth, bottom, mPaint);}
}

通过Span实现动画

 在上一节中,我们已经实现了MutableForegroundColorSpan类,那么如何使用它来实现动画呢?这里我们就要使用到ObjectAnimatorProperty<T1,T2>。我们首先定义MutableForegroundColorSpan使用的property

private static final Property<MutableForegroundColorSpan, Integer> MUTABLE_FOREGROUND_COLOR_SPAN_PROPERTY =new Property<MutableForegroundColorSpan, Integer>(Integer.class, "MUTABLE_FOREGROUND_COLOR_SPAN_FC_PROPERTY") {@Overridepublic void set(MutableForegroundColorSpan alphaForegroundColorSpanGroup, Integer value) {alphaForegroundColorSpanGroup.setForegroundColor(value);}@Overridepublic Integer get(MutableForegroundColorSpan span) {return span.getForegroundColor();}
};

 然后我们就可以使用ObjectAnimatorMutableForegroundColorSpan实现属性动画了。

MutableForegroundColorSpan span = new MutableForegroundColorSpan(255, Color.BLUE);
final SpannableString spannableString = new SpannableString(CONTENT);
spannableString.setSpan(span, 0,4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(span, MUTABLE_FOREGROUND_COLOR_SPAN_PROPERTY, Color.BLACK, Color.RED);
objectAnimator.setEvaluator(new ArgbEvaluator());
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//refreshmTvTextView.setText(spannableString);}
});
objectAnimator.setInterpolator(mSmoothInterpolator);
objectAnimator.setDuration(600);
objectAnimator.start();

 这里还只是单独一个Span实例的动画效果,你可以对多个Span实例进行属性动画,从而实现更加复杂的动画效果。就比如文章开始时的文字逐渐显示的动画效果。
 我们可以给每个字都设置一个MutableForegroundColorSpan实例,并将这些实例都添加到一个对象中,然后在属性动画过程中,乱序设置每个实例的alpha的值,从而达到文字逐渐显现的动画。

public class FireWorkGroup {private final float mProgress;private final ArrayList<MutableForegroundColorSpan> mSpans;private final ArrayList<Integer> mSpanIndexes;public FireWorkGroup() {mProgress = 0;mSpans  = new ArrayList<>();mSpanIndexes = new ArrayList<>();}public void addSpan(MutableForegroundColorSpan span) {span.setAlpha(0);mSpanIndexes.add(mSpans.size());mSpans.add(span);}public void init() {Collections.shuffle(mSpans);}public void setProgress(float progress) {int size = mSpans.size();float total  = 1.0f * size * progress;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 getProgress() {return mProgress;}public static final Property<FireWorkGroup, Float> FIREWORKS_GROUP_PROGRESS_PROPERTY =new Property<FireWorkGroup, Float>(Float.class, "FIREWORKS_GROUP_PROGRESS_PROPERTY") {@Overridepublic void set(FireWorkGroup spanGroup, Float value) {spanGroup.setProgress(value);}@Overridepublic Float get(FireWorkGroup spanGroup) {return spanGroup.getProgress();}};
}

后记

 上述的这些动画都是我在第一篇文章提到的那篇博文中实现过的效果,我在学习过程中又发现了一个实现了很多TextView相关动画的开源项目HTextView。所以接下来的任务就是想使用Span机制去实现这个项目中的一些动画效果。希望大家继续支持我的文章,并积极指出文中的错误。
 !!!源码都在我的github里。

Android Span 进阶相关推荐

  1. Android开发进阶之NIO非阻塞包(一)

    Android开发进阶之NIO非阻塞包 这个系列转载于http://www.android123.com.cn/androidkaifa/695.html,特此说明 对于Android的网络通讯性能的 ...

  2. Android日志[进阶篇]五-阅读错误报告

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  3. Android日志[进阶篇]四-获取错误报告

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  4. Android日志[进阶篇]三-Logcat 命令行工具

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  5. Android日志[进阶篇]一-使用 Logcat 写入和查看日志

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  6. Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈)

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  7. [总]Android高级进阶之路

    个人Android高级进阶之路,目前按照这个目录执行,执行完毕再做扩展!!!!! 一.View的绘制 1)setContentView()的源码分析 2)SnackBar的源码分析 3)利用decor ...

  8. Android中高级进阶开发面试题冲刺合集(七)

    以下主要针对往期收录的面试题进行一个分类归纳整理,方便大家统一回顾和参考.本篇是第七集~ 强调一下:因篇幅问题:文中只放部分内容,全部面试开发文档需要的可在公众号<Android苦做舟>获 ...

  9. Android工程师进阶第五课 多线程锁,线程池和DVM/ART优化

    第09讲:Java 线程优化 偏向锁,轻量级锁.重量级锁 我目前所在的公司是一家跨国企业,总部在瑞典.前段时间公司新开发的一个应用准备发布到应用宝平台.但是在发布之前,需要准备一系列软著相关的证明材料 ...

  10. Android中高级进阶开发面试题冲刺合集(四)

    以下主要针对往期收录的面试题进行一个分类归纳整理,方便大家统一回顾和参考.本篇是第四集~ 强调一下:因篇幅问题:文中只放部分内容,全部面试开发文档需要的可在公众号<Android苦做舟>获 ...

最新文章

  1. python出现套接字创建不成功_python套接字连接在Mac上被拒绝但在Windows
  2. pg GUI Tools
  3. mybatis-plus 错误java.lang.NoClassDefFoundError: org
  4. 在NAnt中加入Vssget 任务
  5. spring mvc学习(12)---使用idea创建第一个maven项目
  6. [Leetcode][第78题][JAVA][子集][位运算][回溯]
  7. 机器人蛮王_盖伦:吊打我老婆,蛮王:我也是,他:被老婆打的不敢出塔
  8. JavaScript生成指定范围内的随机数
  9. 第五章 运输层[练习题+课后习题]
  10. 跟我一起学QT_QT标准对话框_颜色选择框
  11. MiniTable 16 网络版安装部署
  12. 大数据使用的5种主要数据挖掘技术
  13. c语言fat文件系统,【操作系统】简单FAT文件系统实现
  14. php opendir(),PHP opendir()用法及代码示例
  15. The Dominant Color (20)
  16. android手表微信运动,oppo智能手表微信运动如何安装
  17. java对服务器文件操作、获取,删除,下载
  18. 电脑不识别u盘的解决方法
  19. 学术会议日常英语交流_有效的日常会议的3个问题
  20. zuul 详解,带视频

热门文章

  1. javafx 与java,java桌面应用程序和javafx有什么区别?
  2. 【面向代码】学习 Deep Learning(四) Stacked Auto-Encoders(SAE)
  3. shiny datatable child row:shiny表格二级子行的展开与折叠
  4. 程序员的十个经典算法
  5. Kronecker 定理
  6. 从弧长的计算到逆时针与顺时针旋转的定义 (二维空间)
  7. Ctftool:CTF漏洞利用工具
  8. 633.平方数之和(力扣leetcode) 博主可答疑该问题
  9. C语言函数未声明错误,switch 调用函数 错误未定义???
  10. mysql 伪哈希_MySQL技巧--伪哈希索引