編輯:關於Android編程
?在上篇文章《Android Span 架構介紹》,我們講述了Android Span的基本概念和用法,這篇文章我們就來擴展一下我們對Android Span的了解,這一定會使你感到驚奇的,驚歎Android Span竟然還能完成這些的效果,讓你在Android自定義View和動畫方面有更加深刻的理解,可能會幫助你你想出更加簡潔的實現方式。
?本篇文章主要講述一下兩個方面的內容:
?先貼一下本篇文章實現的自定義Span和動畫的效果圖


?我們都知道,自定義View有兩種方式,一種是繼承特定的視圖類,比如你希望修改TextView的行為,所以繼承了TextView;另一種就是直接繼承View或者ViewGroup,這樣可以實現全新的視圖和行為。如同自定義View一樣,你有兩種自定義Span的方法,一種直接繼承特定類型的Span類,比如ForegroundColorSpan等,這樣你可以在這些類的基礎上進行修改;另一種就是繼承ReplaceSpan這樣的抽象類或者實現LetterLineBackgroundSpan這樣的接口,你只要實現它給出的接口,就可以實現新的效果。
?我們先來講解第一種方式。直接繼承現有的Span。文章開頭時展示的ActionBar動畫就是通過繼承ForegroundSpan來實現的。
?我們主要重載了updateDrawsState和getForegroundColor,這樣就可以通過改變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;
}
@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

<code><code><code><code><code><code>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; string="" charat="extractText(text," i="" float="" charwidth="paint.measureText(charAt);" charx="" private="" charsequence="" int="" return="" pre="">
?我們可以看到,我們要實現兩個函數: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
private static final PropertyMUTABLE_FOREGROUND_COLOR_SPAN_PROPERTY = new Property (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 mSpans;
private final ArrayList 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 FIREWORKS_GROUP_PROGRESS_PROPERTY =
new Property(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裡。
Android自定義ListView實現下拉刷新
首先呈上效果圖當今APP,哪個沒有點滑動刷新功能,簡直就太落伍了。正因為需求多,因此自然而然開源的也就多。但是若想引用開源庫,則很麻煩,比如PullToRefreshVi
Android官方開發文檔之從Eclipse遷移到Android Studio開發篇
1、前言從Eclipse遷移項目到Android Studio需要適應一個新的項目結構,建立系統和IDE的功能。為了簡化遷移過程,Android Studio提供了重要工
深入分析PMS服務(一)
我們分析一些源碼的實現.首先從PMS服務開始.PMS服務即PackageManagerService,主要用來進行APK的管理任務.但是今天,我們並不直接分析PMS的源碼
Android消息機制Handler的工作過程詳解
綜述 在Android系統中,出於對性能優化的考慮,對於Android的UI操作並不是線程安全的。也就是說若是有多個線程來操作UI組件,就會有可能導致線程安全問題。所以