Android的ColorDrawable源碼解析

ColorDrawable源碼分析

ColorDrawable是Drawable子類中最簡單的,表明一種顏色圖。 在代碼中使用是很是簡單的。通常對於純色背景均可以使用ColorDrawable。android

<?xml version="1.0" encoding="utf-8"?>
<color
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#0000ff">
</color>
這樣就定義了一個純藍色的背景
複製代碼

而後就能夠在Java代碼中或者xml中使用canvas

Drawable d = getResources().getDrawable(R.drawable.color_drawable);
Log.i(TAG,  d.getClass().getSimpleName());//輸出ColorDrawable

在xml中,就是好比某個組件的background之類的屬性就能夠把資源引用加上去,系統就會加載該資源
複製代碼

(一)前一篇對Drawable的分析中,有一個setColorFilter方法,能夠改變顏色,那麼咱們看一下究竟是不是這麼回事?

float[] array = {0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0};//顏色矩陣計算,從藍色轉變爲紅色
d.setColorFilter(new ColorMatrixColorFilter(array));
parent.setBackground(d);//從新設置背景色
複製代碼

執行上述代碼以後,發現顏色沒變,仍是藍色!WTF?Drawable源碼裏面明明寫的是經過setColorFilter就能夠改變顏色啊!那咱們要使用ColorDrawable改變顏色怎麼辦?bash

parent.setBackground(new ColorDrawable(Color.RED));//成功變成紅色
複製代碼

顯然,新建立一個ColorDrawable固然沒問題,可是爲何setColorFilter沒有用呢?ide

//ColorDrawable開頭的註釋內容
A specialized Drawable that fills the Canvas with a specified color.
Note that a ColorDrawable ignores the ColorFilter.//會忽略ColorFilter
複製代碼

原來是這樣子,它會忽略ColorFilter的值,那麼究竟是在哪裏處理的?由於ColorFilter是設置在Paint上的,因此咱們看一會兒類的draw方法,可能會有什麼發現。源碼分析

public void draw(Canvas canvas) {
        // 獲取ColorFilter
        final ColorFilter colorFilter = mPaint.getColorFilter();
        // 判斷使用的顏色透明度是否爲0,若是爲0,則不必繪製背景了
        // 這裏須要注意,若是動態設置顏色的時候沒有明確透明度,那麼這裏就是按照24位RGB來計算的,最後就是0!!!
        if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) {
            if (colorFilter == null) {
                mPaint.setColorFilter(mTintFilter);
            }
            // 關鍵點在這裏啊,從新設置了顏色值,這樣就和ColorFilter無關了
            mPaint.setColor(mColorState.mUseColor);
            // 能夠看到,ColorDrawable是按照矩形繪製的
            canvas.drawRect(getBounds(), mPaint);

            // Restore original color filter.
            // 再把ColorFilter保存回來
            mPaint.setColorFilter(colorFilter);
        }
    }
複製代碼

到這裏,咱們就知道對於ColorDrawable爲何設置ColorFilter無效了。ui

(二)接下來看,ConstantState在這裏的子類實現,ColorState

int mBaseColor; // 基礎顏色,和透明度獨立
int mUseColor;  // 會被透明度影響的基礎顏色
複製代碼

剛纔咱們在draw方法裏面用到的也是mUseColor,所以,咱們能夠這樣理解: mBaseColor是保存了set後的顏色 mUseColor是保存每次變化後的顏色 爲何這麼說呢?由於從源碼中搜索能夠看出,mBaseColor只有在setColor和updateFromTypedArray中才有更新this

當顏色不一致時才設置並重繪自身,所以能夠經過setColor的方式改變顏色
public void setColor(@ColorInt int color) {
        if (mColorState.mBaseColor != color || mColorState.mUseColor != color) {
            mColorState.mBaseColor = mColorState.mUseColor = color;
            invalidateSelf();
        }
    }
從xml中獲取屬性值    
state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor);
複製代碼

那麼改變透明度就表示在mUseColor上面作動做麼?spa

public void setAlpha(int alpha) {
        alpha += alpha >> 7;   // make it 0..256
        final int baseAlpha = mColorState.mBaseColor >>> 24;//無符號右移,因此前24位都是0,最後8位是透明度
        final int useAlpha = baseAlpha * alpha >> 8;
        final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
        // 先左移8位去掉8位透明度,再無符號右移8位。
        // 前8位0,後24爲RGB顏色,再或透明度左移24位,最後獲得新的32位ARGB顏色
        if (mColorState.mUseColor != useColor) {
            mColorState.mUseColor = useColor;
            invalidateSelf();
        }
    }
複製代碼

這麼一大段左右移運算到底在幹啥?爲啥不能簡單點?code

useColor & 0xFFFFFF | alpha << 24//這樣不行麼?
複製代碼

說實話。。我沒看懂透明度那部分爲何要這麼計算。。Google的工程師仍是天資聰穎 可是咱們也能夠看到,全部的改變都是在mUserColor上進行,mBaseColor是一個基準顏色。xml

(三)最關鍵的mutate方法,它到底作了什麼?

private boolean mMutated;//保存是否改變過的布爾值
public Drawable mutate() {
        // 若是沒有改變過,而且是同一個Drawable(super.mutate方法直接返回this)
        if (!mMutated && super.mutate() == this) {
            // 能夠看到直接新建了一個ColorState,這樣就不和其餘ColorDrawable共享狀態,所以不會相互影響,至關於深拷貝
            mColorState = new ColorState(mColorState);
            // 標記已改變
            mMutated = true;
        }
        // mColorState是成員變量,所以this是一個已經改變後的ColorDrawable
        return this;
    }
複製代碼

(四)那改變了以後還能不能複用呢?有沒有改變mMutated變量的方法呢?

public void clearMutated() {
        super.clearMutated();
        mMutated = false;
    }
   // 能夠看到該方法是能夠清除標記位的,可是實際因爲Hide,是沒法調用的。因此一旦mutate調用了以後,就沒法回頭了哦。
複製代碼

(五)那麼若是我想再建立一個如出一轍的ColorDrawable應該怎麼辦呢?

@Override
        public Drawable newDrawable() {
            return new ColorDrawable(this, null);
        }

        @Override
        public Drawable newDrawable(Resources res) {
            return new ColorDrawable(this, res);
        }
      //  這個this指代的就是ColorState,由於該方法是在ColorState類中定義的。
複製代碼

那麼在Java代碼中,就可使用

d.getConstantState().newDrawable();
// 就能夠建立一個和當前狀態如出一轍的ColorDrawable對象,可是他們仍是共享一個ColorState哦。
複製代碼

對於最簡單的ColorDrawable須要瞭解的就這麼多了。下一節將討論比ColorDrawable稍微複雜一點的ShapeDrawable。敬請期待。

相關文章
相關標籤/搜索