在上篇文章《Android Span 架构介绍》,咱们讲述了Android Span的基本概念和用法,这篇文章咱们就来扩展一下咱们对Android Span的了解,这必定会使你感到惊奇的,惊叹Android Span居然还能完成这些的效果,让你在Android自定义View和动画方面有更加深入的理解,可能会帮助你你想出更加简洁的实现方式。
本篇文章主要讲述一下两个方面的内容:git
自定义Android Spangithub
使用Android Span实现动画canvas
先贴一下本篇文章实现的自定义Span和动画的效果图架构
咱们都知道,自定义View有两种方式,一种是继承特定的视图类,好比你但愿修改TextView
的行为,因此继承了TextView
;另外一种就是直接继承View
或者ViewGroup
,这样能够实现全新的视图和行为。如同自定义View同样,你有两种自定义Span的方法,一种直接继承特定类型的Span类,好比ForegroundColorSpan
等,这样你能够在这些类的基础上进行修改;另外一种就是继承ReplaceSpan
这样的抽象类或者实现LetterLineBackgroundSpan
这样的接口,你只要实现它给出的接口,就能够实现新的效果。
咱们先来说解第一种方式。直接继承现有的Span
。文章开头时展现的ActionBar
动画就是经过继承ForegroundSpan
来实现的。
咱们主要重载了updateDrawsState
和getForegroundColor
,这样就能够经过改变setAlpha
函数来改变颜色,让字体从透明(alpha为0)到某个特定颜色。dom
public class MutableForegroundColorSpan extends ForegroundColorSpan { private int mAlpha = 255; private int mForegroundColor; public MutableForegroundColorSpan(int alpha,int color) { super(color); mAlpha = alpha; mForegroundColor = color; } @Override public 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; } @Override public int getForegroundColor() { return Color.argb(mAlpha,Color.red(mForegroundColor),Color.green(mForegroundColor),Color.blue(mForegroundColor)); } }
第二种方法是继承Span
架构中的抽象类或者是实现特定接口。须要注意的是,在上一篇文章中说的CharacterStyle
,ParagraphStyle
.UpdateAppearance
和UpdateLayout
都是没有函数的,因此,咱们没法直接继承或者实现它们。除了第一篇文章中所介绍的那些Span
可使用第一种方法进行继承。咱们通常都继承或者实现MetricAffectingSpan
,ReplacementSpan
或者LineBackgroundSpan
。
好比咱们想给每一个字都添加一个不一样颜色的背景,咱们就能够继承ReplacementSpan
ide
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)); } } @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) { 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(); } }
咱们能够看到,咱们要实现两个函数:getSize
和draw
。getSize
是得到字体的长度的,因此通常都是直接使用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); } @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) { //只绘制了外围矩形,没有绘制文字。 canvas.drawRect(x, top, x + mWidth, bottom, mPaint); } }
在上一节中,咱们已经实现了MutableForegroundColorSpan
类,那么如何使用它来实现动画呢?这里咱们就要使用到ObjectAnimator
和Property<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") { @Override public void set(MutableForegroundColorSpan alphaForegroundColorSpanGroup, Integer value) { alphaForegroundColorSpanGroup.setForegroundColor(value); } @Override public Integer get(MutableForegroundColorSpan span) { return span.getForegroundColor(); } };
而后咱们就可使用ObjectAnimator
对MutableForegroundColorSpan
实现属性动画了。字体
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() { @Override public void onAnimationUpdate(ValueAnimator animation) { //refresh mTvTextView.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") { @Override public void set(FireWorkGroup spanGroup, Float value) { spanGroup.setProgress(value); } @Override public Float get(FireWorkGroup spanGroup) { return spanGroup.getProgress(); } }; }
上述的这些动画都是我在第一篇文章提到的那篇博文中实现过的效果,我在学习过程当中又发现了一个实现了不少TextView
相关动画的开源项目HTextView。因此接下来的任务就是想使用Span
机制去实现这个项目中的一些动画效果。但愿你们继续支持个人文章,并积极指出文中的错误。
!!!源码都在个人github里。