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。敬请期待。

相关文章
相关标签/搜索