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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值