Target相关
通常我们是调用requestBuilder#into(ImageView),接下来看看,ImageView是如果转化为Target的。
// RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
// ... 省略scaleType相关代码
// 可以看到是通过GlideContext构建一个ViewTarget,内部是使用imageViewTargetFactory来构建的
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
// GlideContext.java
// 通过下方代码分析,可以知道是在Glide的构造函数中传进来的
private final ImageViewTargetFactory imageViewTargetFactory;
public GlideContext(
...
@NonNull ImageViewTargetFactory imageViewTargetFactory,
...) {
super(context.getApplicationContext());
...
this.imageViewTargetFactory = imageViewTargetFactory;
...
}
public <X> ViewTarget<ImageView, X> buildImageViewTarget(ImageView imageView, Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
// Glide.java
Glide(...){
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext = new GlideContext(
...
imageViewTargetFactory,
...);
}
// ImageViewTargetFactory.java
public class ImageViewTargetFactory {
// 通过判断不同的类型,实例化不同的ViewTarget
public <Z> ViewTarget<ImageView, Z> buildTarget(
@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
通常我们是通过一个url去加载图片的,由上面RequestManger#load(string)代码可以知道,RequestBuilder的transcodeClass是Drawable.class,所以最后得到的target是DrawableImageViewTarget。
从Glide源码分析二——Request相关中的SingleRequest#begin()可知,如果没有设置overrideWidth/overrideHeight,会通过viewTarget#getSize(SizeReadyCallback)获取大小,获取大小后在回调SizeReadyCallback#onSizeReady()通知SingleRequest加载图片。
public abstract class ViewTarget<T extends View, Z> extends BaseTarget<Z> {
protected final T view;
private final SizeDeterminer sizeDeterminer;
public ViewTarget(@NonNull T view) {
this.view = Preconditions.checkNotNull(view);
sizeDeterminer = new SizeDeterminer(view);
}
public void getSize(@NonNull SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
static final class SizeDeterminer {
private final View view;
private final List<SizeReadyCallback> cbs = new ArrayList<>();
private SizeDeterminerLayoutListener layoutListener;
SizeDeterminer(@NonNull View view) {
this.view = view;
}
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
// 如果view已经可以获取大小了,则回调SizeReadyCallback#onSizeReady(w,h)通知加载图片
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
return;
}
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
// 通过添加PreDrawListener来获取大小,获取大小后会回调SizeReadyCallback#onSizeReady(w,h)通知加载图片
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
// 获取view的高度
private int getTargetHeight() {
int verticalPadding = view.getPaddingTop() + view.getPaddingBottom();
LayoutParams layoutParams = view.getLayoutParams();
int layoutParamSize = layoutParams != null ? layoutParams.height : PENDING_SIZE;
return getTargetDimen(view.getHeight(), layoutParamSize, verticalPadding);
}
private int getTargetDimen(int viewSize, int paramSize, int paddingSize) {
int adjustedParamSize = paramSize - paddingSize;
// layoutParam中的Size可用就用paramSize
if (adjustedParamSize > 0) {
return adjustedParamSize;
}
// 如果view申请了绘制(例如调用了requestLayout),则返回0(无效尺寸,后面会去测量)
if (waitForLayout && view.isLayoutRequested()) {
return PENDING_SIZE;
}
// 如果view已经有了宽高,则返回view的宽高
int adjustedViewSize = viewSize - paddingSize;
if (adjustedViewSize > 0) {
return adjustedViewSize;
}
// 如果view没有申请绘制,且尺寸是wrap_content,则返回屏幕宽高的最大值
if (!view.isLayoutRequested() && paramSize == LayoutParams.WRAP_CONTENT) {
return getMaxDisplayLength(view.getContext());
}
// 不满足上述条件则返回无效尺寸
return PENDING_SIZE;
}
void checkCurrentDimens() {
if (cbs.isEmpty()) {
return;
}
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (!isViewStateAndSizeValid(currentWidth, currentHeight)) {
return;
}
// 获取到view的大小后,通过SizeReadyCallback#onSizeReady(w,h)通知加载图片
notifyCbs(currentWidth, currentHeight);
// 回调完后清除之前创建的PreDrawListener和添加的SizeReadyCallback
clearCallbacksAndListener();
}
private static final class SizeDeterminerLayoutListener
implements ViewTreeObserver.OnPreDrawListener {
private final WeakReference<SizeDeterminer> sizeDeterminerRef;
SizeDeterminerLayoutListener(@NonNull SizeDeterminer sizeDeterminer) {
sizeDeterminerRef = new WeakReference<>(sizeDeterminer);
}
@Override
public boolean onPreDraw() {
// 这时可以正常获取到大小了,通过SizeDeterminer去获取大小
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
return true;
}
}
}
}
ViewTarget通过SizeDeterminer获取大小,当view已经有大小或可以通过LayoutPrams获取大小时,直接回调;否则通过添加PreDrawListener来获取大小,之后再回调。
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
implements Transition.ViewAdapter {
// 其他onLoadXxx(...)都差不多
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
// 当资源获取成功时会回调此方法
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
// 通知子类加载资源,例如DrawableImageViewTarget就是直接调用ImageView#setImageDrawable(resource)
setResource(resource);
// 如果资源是动画的话,则开启动画
maybeUpdateAnimatable(resource);
}
@Override
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
private void maybeUpdateAnimatable(@Nullable Z resource) {
if (resource instanceof Animatable) {
animatable = (Animatable) resource;
animatable.start();
} else {
animatable = null;
}
}
protected abstract void setResource(@Nullable Z resource);
}