在 Android 开发中,ImageView 是一个用户界面控件,用于在应用中显示图片。它是 Android UI 组件库中一个非常基础和常用的部分。使用 ImageView,你可以在屏幕上显示来自不同来源的图像,比如位图文件、绘图资源 drawable、网络来源或者相机拍摄的图片。
在实际的开发过程中,我们会在 Java 或 Kotlin 代码中调用 setImageResource()、setImageBitmap()、setImageDrawable() 等方法来设置或改变图片。
但我最近在检测应用的性能时,发现 imageView 在加载图片竟有一些耗时,于是进入源码来看看这几个给 imageView 设置图片的方法都有什么区别
public void setImageResource(@DrawableRes int resId) {
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
updateDrawable(null);
mResource = resId;
mUri = null;
resolveUri();
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}
在 setImageResource 方法中,首先 updateDrawable() 做了重置操作,后面给成员变量 mResource 赋值,接着执行 resolveUri() 这个方法对 mResource 进行解析
private void resolveUri() {
Drawable d = null;
if (mResource != 0) {
try {
// 根据传进来的资源ID去获取对应的Drawable(耗时)
d = mContext.getDrawable(mResource);
} catch (Exception e) {
// Don't try again.
mResource = 0;
}
} else if (mUri != null) {
d = getDrawableFromUri(mUri);
if (d == null) {
// Don't try again.
mUri = null;
}
} else {
return;
}
updateDrawable(d);
}
resolveUri() 方法中会将刚刚传进来的 mResource 去获取对应的 Drawable,获取到 Drawble 后通过调用 updateDrawable() 来更新 imageView 中的图像
public void setImageBitmap(Bitmap bm) {
mDrawable = null;
if (mRecycleableBitmapDrawable == null) {
mRecycleableBitmapDrawable = new BitmapDrawable(mContext.getResources(), bm);
} else {
mRecycleableBitmapDrawable.setBitmap(bm);
}
setImageDrawable(mRecycleableBitmapDrawable);
}
setImageBitmap 代码非常少,首先确定有一个 BitmapDrawable 对象,将传进来的 Bitmap 赋值于此,然后调用 setImageDrawable() 方法
public void setImageDrawable(@Nullable Drawable drawable) {
if (mDrawable != drawable) {
mResource = 0;
mUri = null;
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
updateDrawable(drawable);
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}
}
在 setImageDrawable 方法中,直接将传进来的 Drawable 来调用 updateDrawable() 方法来更新imageView() 中的图像
总结:
用这三种方法去更新 imageView 最终都会调用到 updateDrawable() 这个方法,但是在 setImageResource() 中的 resolveUri() 方法涉及到了资源获取:mContext.getDrawable(),这个是耗时的,所以在短时间内调用大量的 setImageResource 可能会造成应用卡顿。
解决方法:
将资源 ID 获取到的 Drawable 进行缓存或者设置为成员变量,再调用 setImageDrawable() 即可。这样子能避免资源获取而造成的卡顿。
此外,ImageView 还提供了其他方法,如 setImageURI(Uri uri),用于通过 URI 设置图像,但无论哪种设置图像的方法,最终都是通过 Drawable 来实现图像的渲染。所以在短时间内大量设置imageView 图像时,需要优先缓存 Drawable 来加载图像来保证性能是最佳的