在上篇文章《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裏。