Picasso 图片加载展示的最后一步是传入 PicassoDrawable 对象,调用 ImageVIew 的 setImageDrawable 方法。这其实和我们平时用 ImageView 展示图片有些不同。项目中使用,更多的是调用 setImageResource 来传入一个资源 ID,或者在 xml 布局中指定资源。
这使得我想去探究下这两个方法调用有何不同,以及对 xx-Drawable 理解。
Drawable
代表着这样一个东西,它有绘制的功能,我们经常用它来表示图片,形状,资源,并最终展示到屏幕上。也可以这么理解,它是仅保留了 draw 功能的 View。
因为是抽象类,所以有几个抽象方法在子类里是要实现的,
- setAlpha
- setColorFilter
- draw
Drawable 实现的形式有很多,意味着对应的子类也有很多,具体的形式有,
- Bitmap
- Nine Patch
- Vector
- Shape
- Layers
- States
- Levels
- Scale
这个类应该和我们 res 包下面的 drawable 里的 xml 文件是对应的。例如定义一个 shape 标签的 drawable 文件就对应着 Shape 形式,其他应该类似能对应上(当然这基于猜测,有待后续分析验证)
就我理解的 Drawable 这个抽象类,重点应该就在 draw 方法的实现,所以如果想要展示一些东西,可以利用 Drawable 绘制,因为通过 draw 方法能持有画布引用,我们暂且不管画布如何将内容展示在屏幕上,就认准有了画布就能画东西,画的内容就能展示出来。
BitmapDrawable
PicassoDrawable 继承自 BitmapDrawable,它的特殊性在于包裹了一个 bitmap,这个 bitmap 的数据来源可以是文件,输入流,xml 或者直接 Bitmap 对象。显然是围绕 Bitmap 展开的,从 draw 方法可以看到最终调用 canvas.drawBitmap 来绘制。
由此分析 PicassoDrawable 的本质也是类似,也就是说加载过来的 Bitmap 最终会以 canvas.drawBitmap 的方式最终展现。
ImageView
再回过头来看下 setImageDrawable 方法,
public void setImageDrawable(@Nullable Drawable drawable) {
if (mDrawable != drawable) {
mResource = 0;
updateDrawable(drawable);
invalidate();
}
}
大致的思路是更新 mDrawable 对象,并触发后续的 onDraw 步骤。其实在 View 的工作流中,和展示最直接相关的也就是 onDraw 了,
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawable == null) {
return;
}
if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
mDrawable.draw(canvas);
} else {
final int saveCount = canvas.getSaveCount();
canvas.save();
canvas.translate(mPaddingLeft, mPaddingTop);
if (mDrawMatrix != null) {
canvas.concat(mDrawMatrix);
}
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
从 ImageView 的 onDraw 方法中可以看出 mDrawable 其实是个很关键属性,要是不存在的话,onDraw 也就没事可做了。代码再往下执行,其实就是调用了 Drawable 的 draw 方法,结合前面以 BitmapDrawable 的为例的分析,最终就是 drawBitmap,所以最终图片得以展示。
那么 setImageResource 方法呢?
public void setImageResource(@DrawableRes int resId) {
updateDrawable(null);
mResource = resId;
mUri = null;
resolveUri();
invalidate();
}
private void resolveUri() {
if (mDrawable != null) {
return;
}
Drawable d = null;
if (mResource != 0) {
d = mContext.getDrawable(mResource);
} else if (mUri != null) {
d = getDrawableFromUri(mUri);
} else {
return;
}
updateDrawable(d);
}
首先会将 mDrawable 置为空,接着通过 Context 对象将图片资源 ID 转为 Drawable 对象,然后更新给 mDrawable 对象,最后依然会触发 onDraw 方法的调用来完成绘制。
由此可见,平常虽说直接引用图片资源,但实际会最终以 Drawable 的形式用来展示,资源形式不过便于我们开发使用罢了。
至此,对 ImageView 我又有了新的理解,它之所以能展示图片,并不在于这个控件的特殊性,而在于 Drawable 的作用。
网友评论