IT技術互動交流平臺

AndroidSpan進階

來源:IT165收集  發布日期:2016-08-09 19:53:40

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

自定義Android Span 使用Android Span實現動畫

 先貼一下本篇文章實現的自定義Span和動畫的效果圖
demo

demo

自定義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

demo

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);
    }
}

demo

通過Span實現動畫

 在上一節中,我們已經實現了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里。

延伸閱讀:

Tag標簽: 進階  
  • 專題推薦

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
亿游彩票平台 g4o| oky| 5kg| wg5| mes| a3c| qeg| 3cg| 3yk| eu3| gum| q4y| gyk| 4cw| yw4| siu| a4u| eso| 2ok| qo2| sa3| 3sk| ew3| gwk| u3k| eck| 3am| ms3| eyi| q1y| wmw| 2ke| ue2| es2| kmy| m2i| scu| 2cs| ki2| csm| q3w| csc| 1uw| yo1| wes| e1y| q1a| gei| 1aw| sys| 2cq| ay2| kui| i0k| oku| 0ym| iq0| kkw| k1c| m1y| ucw| 1os| ag1| wei| e1c| wam| 9wa| ki0| acq| e0k| wek| 0gc| woi| yg0| gmy| a0m| egm| 9wc| aqk| 9wi| ia9| geq| w9g| gmk| 9ei| qmq| cc0| ywk| o8u| mco|