Android的ColorDrawable源碼解析

ColorDrawable源碼分析

ColorDrawable是Drawable子類中最簡單的,代表一種顏色圖。
在代碼中使用是非常簡單的。一般對於純色背景都可以使用ColorDrawable。

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

preview的效果
然後就可以在Java代碼中或者xml中使用

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改變顏色怎麼辦?

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

顯然,新創建一個ColorDrawable當然沒問題,但是爲什麼setColorFilter沒有用呢?

//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無效了。

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

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

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

當顏色不一致時才設置並重繪自身,因此可以通過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上面做動作麼?

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

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

useColor & 0xFFFFFF | alpha << 24//這樣不行麼?

說實話。。我沒看懂透明度那部分爲什麼要這麼計算。。Google的工程師還是天資聰穎
但是我們也可以看到,所有的改變都是在mUserColor上進行,mBaseColor是一個基準顏色。

(三)最關鍵的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。敬請期待。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章