Target相關(guān)
Target相關(guān)
通常我們是調(diào)用requestBuilder#into(ImageView)憾股,接下來(lái)看看环葵,ImageView是如果轉(zhuǎn)化為Target的咕缎。
// RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
// ... 省略scaleType相關(guān)代碼
// 可以看到是通過(guò)GlideContext構(gòu)建一個(gè)ViewTarget郭蕉,內(nèi)部是使用imageViewTargetFactory來(lái)構(gòu)建的
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
// GlideContext.java
// 通過(guò)下方代碼分析劝赔,可以知道是在Glide的構(gòu)造函數(shù)中傳進(jìn)來(lái)的
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 {
// 通過(guò)判斷不同的類型烘豹,實(shí)例化不同的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)");
}
}
}
通常我們是通過(guò)一個(gè)url去加載圖片的瓜贾,由上面RequestManger#load(string)代碼可以知道,RequestBuilder的transcodeClass是Drawable.class携悯,所以最后得到的target是DrawableImageViewTarget祭芦。
從Glide源碼分析二——Request相關(guān)中的SingleRequest#begin()可知,如果沒(méi)有設(shè)置overrideWidth/overrideHeight憔鬼,會(huì)通過(guò)viewTarget#getSize(SizeReadyCallback)獲取大小龟劲,獲取大小后在回調(diào)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已經(jīng)可以獲取大小了逊彭,則回調(diào)SizeReadyCallback#onSizeReady(w,h)通知加載圖片
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
return;
}
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
// 通過(guò)添加PreDrawListener來(lái)獲取大小咸灿,獲取大小后會(huì)回調(diào)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申請(qǐng)了繪制(例如調(diào)用了requestLayout),則返回0(無(wú)效尺寸侮叮,后面會(huì)去測(cè)量)
if (waitForLayout && view.isLayoutRequested()) {
return PENDING_SIZE;
}
// 如果view已經(jīng)有了寬高避矢,則返回view的寬高
int adjustedViewSize = viewSize - paddingSize;
if (adjustedViewSize > 0) {
return adjustedViewSize;
}
// 如果view沒(méi)有申請(qǐng)繪制,且尺寸是wrap_content,則返回屏幕寬高的最大值
if (!view.isLayoutRequested() && paramSize == LayoutParams.WRAP_CONTENT) {
return getMaxDisplayLength(view.getContext());
}
// 不滿足上述條件則返回?zé)o效尺寸
return PENDING_SIZE;
}
void checkCurrentDimens() {
if (cbs.isEmpty()) {
return;
}
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (!isViewStateAndSizeValid(currentWidth, currentHeight)) {
return;
}
// 獲取到view的大小后审胸,通過(guò)SizeReadyCallback#onSizeReady(w,h)通知加載圖片
notifyCbs(currentWidth, currentHeight);
// 回調(diào)完后清除之前創(chuàng)建的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() {
// 這時(shí)可以正常獲取到大小了亥宿,通過(guò)SizeDeterminer去獲取大小
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
return true;
}
}
}
}
ViewTarget通過(guò)SizeDeterminer獲取大小,當(dāng)view已經(jīng)有大小或可以通過(guò)LayoutPrams獲取大小時(shí)砂沛,直接回調(diào)烫扼;否則通過(guò)添加PreDrawListener來(lái)獲取大小,之后再回調(diào)碍庵。
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);
}
// 當(dāng)資源獲取成功時(shí)會(huì)回調(diào)此方法
@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就是直接調(diào)用ImageView#setImageDrawable(resource)
setResource(resource);
// 如果資源是動(dòng)畫的話,則開(kāi)啟動(dòng)畫
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);
}