Android的Drawable源码分析

Drawable源码分析

迁移到掘金
Android中会经常使用Drawable作为某一个View的背景,既可以是外部图片加载的,也可以是自己通过xml绘制的,也可以是自定义一个Drawable子类实现的。那么我们就从Drawable类开始,分析其在Android中是如何完成这些功能的。
本系列文章以API28作为源码分析基准。
该系列文章初步设计为8篇,更新时间待定。。
通过查看Drawable子类,可以发现其拥有41个子类(其中包含了一部分Compat),可见Drawable在Android中地位还是十分重要的,系统中提供了大量的不同类型的Drawable供开发者使用。根据源码中的注释,可以看到Drawable主要可以分为如下几个类别

(1) Bitmap: 最简单的,用于加载png/jpeg.
(2) Nine Patch:png的拓展,可以拉伸并在其中放置信息
(3) Vector: xml中定义的一系列点线集合,拉伸几乎不会有画质损失
(4) Shape: 代替原始的bitmap,在一些改变大小的场景比较合适
(5) Layers: 复合图,多个图绘制在一起
(6) States: 复合图,按照不同状态进行显示
(7) Levels: 复合图,根据层级进行选择
(8) Scale:  具有单个子级可绘制的复合可绘制文件,其总体大小根据当前级别进行修改

(一)基础信息
通过AS的structure窗口,可以看到Drawable类的组成,主要包含五大部分
1.interface Callback:当我们需要实现带动画效果的图时,需要实现该接口
2.public static abstract class ConstantState:保存图的状态,这个是比较关键的类。举个例子,当我们使用同一个图加载的Drawable时,它们都对应同一个ConstantState,当改变其中一个View的显示状态时,其他引用该图的View也会受到影响。如果想像深拷贝那样来一个Drawable,则可以通过调用mutate方法后再对Drawable进行处理,则他们之间不会相互影响。这里的原理在后续的文章中会涉及。
3.一些处理方法Method
4.一些property
5.一些Field
Field和Property的区别:Field是外部不可见的,没有set/get方法的属性;Property是存在set/get方法,外部可以接触到的属性
(二)关键方法
全局搜索,可以发现整个Drawable类只有几个方法是abstract的,是需要子类去实现的,其他大部分方法都已经有了具体实现,子类只需要去调用就可以获得相应的功能。
1.draw方法
抽象方法。具体的绘制工作交给子类去完成。并且绘制的时候会根据其bound参数进行绘制,如果没有设置任何bound参数,则该drawable的可视面积为0。具体原因为Drawable类初始化时有一个Rect类表示Drawable的绘制区域,如果没有设置bound,则该Rect为0

private static final Rect ZERO_BOUNDS_RECT = new Rect();
private Rect mBounds = ZERO_BOUNDS_RECT;
//通过该方法改变Rect大小
public void setBounds(int left, int top, int right, int bottom) {
        Rect oldBounds = mBounds;

        if (oldBounds == ZERO_BOUNDS_RECT) {
            oldBounds = mBounds = new Rect();
        }

        if (oldBounds.left != left || oldBounds.top != top ||
                oldBounds.right != right || oldBounds.bottom != bottom) {
            if (!oldBounds.isEmpty()) {
                // first invalidate the previous bounds
                invalidateSelf();
            }
            mBounds.set(left, top, right, bottom);
            onBoundsChange(mBounds);//protected空方法,子类实现当大小发生改变时的逻辑
        }
    }

2.setAlpha方法
抽象方法。用于设置透明度,取值0-255,255表示完全不透明,0表示透明。
3.setColorFilter方法
抽象方法。如果设置了ColorFilter,则Drawable中的每一个像素都会进行相应的计算,变成对应的颜色。通过设置null来移除ColorFilter。
该方法一般都是利用ColorMatrixColorFilter来实现,传递一个4*5的数组表示运算矩阵。
4.getIntrinsicWidth等方法
基类中返回-1,如果子类实现,需要更新其数值。诸如BitmapDrawable就在该方法返回实际宽度。
5.getMinimumWidth等方法
该方法返回最小值,会调用getIntrinsicWidth方法和0比较,返回值非负。当用在背景时,该值建议为视图宽度的最小值。
6.mutate方法
字面意思:改变。基类中直接返回这个Drawable

public @NonNull Drawable mutate() {
        return this;
    }

前面我们讲过ConstantState类是一个抽象类,用于记录一个Drawable的各类属性,在Drawable的子类中,也同样有一个ConstantState的静态子类实现。这样,当调用mutate方法的时候,就会创建一个新的ConstantState给Drawable使用,从而不和其他Drawable共用同一个State从而相互影响。
从mutate方法的注释我们也能看出一二

Make this drawable mutable. This operation cannot be reversed. A mutable
drawable is guaranteed to not share its state with any other drawable.
This is especially useful when you need to modify properties of drawables
loaded from resources. By default, all drawables instances loaded from
the same resource share a common state; if you modify the state of one
instance, all the other instances will receive the same modification.
让Drawable可变。这个操作不可逆。一个可变的Drawable不和其他Drawable共享状态。
当你需要改变从resource中加载的drawable属性时十分有用。
默认情况下,从同一个资源中加载的drawable共享一个状态,如果你改变了一个实例的状态,那么其他所有的实例都会受到影响

(三)关键类
1.StateSet类
该类看名字就可以猜出是表示View的状态的集合,其中定义了View的各个状态(正整数)
在Drawable类中,有一个成员变量mStateSet,可以看到是数组形式存在的

private int[] mStateSet = StateSet.WILD_CARD;
//对应的get/set方法
public boolean setState(@NonNull final int[] stateSet) {
        if (!Arrays.equals(mStateSet, stateSet)) {
            mStateSet = stateSet;
            return onStateChange(stateSet);//子类实现,当状态变化时的逻辑
        }
        return false;
    }
public @NonNull int[] getState() {
        return mStateSet;
    }
//StateSet类中的变量
//定义一个保存状态的数组,当诸如StateListDrawable子类使用时,可以将状态保存在其中
public static final int[] WILD_CARD = new int[0];

2.ConstantState类
直白翻译就是常量状态类。该类用于保存一些Drawable之间共享的属性和数据。其中提供了一些抽象方法供子类实现。
public abstract @NonNull Drawable newDrawable(); //根据当前状态创建一个新的Drawable对象,还有其他的重载方法

那么mutate和newDrawable有啥区别?mutate方法会创建一个同样的ConstantState给Drawable,类似于深拷贝,这样改变mutate的drawable属性就不会影响其他drawable;而newDrawable创建出来的还是使用同样的ConstantState属性,是会相互影响的。

3.PorterDuff类
以两个大佬名字命名的类,描述了12种合成操作符,表示如何控制源与目标的颜色合成结果。

以上就是Drawable基类需要了解的一些基础知识,下一篇我们将从最简单的ColorDrawable开始分析每一个子类的具体实现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值