Glide target机制
首先给出一张类图:
ViewTarget
上面的Target是接口的定义,而BaseTarget只是对Target的抽象类,没啥分析的。
ViewTarget有两个知识点可以分析:
- view大小(长宽)的计算
- view状态的监听
view大小(长宽)的计算
这个就是SizeDeterminer
内部类的事情了。
主要的代码:
private boolean isViewStateAndSizeValid(int width, int height) {
return isDimensionValid(width) && isDimensionValid(height);
}
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 getTargetWidth() {
int horizontalPadding = view.getPaddingLeft() + view.getPaddingRight();
LayoutParams layoutParams = view.getLayoutParams();
int layoutParamSize = layoutParams != null ? layoutParams.width : PENDING_SIZE;
return getTargetDimen(view.getWidth(), layoutParamSize, horizontalPadding);
}
private int getTargetDimen(int viewSize, int paramSize, int paddingSize) {
int adjustedParamSize = paramSize - paddingSize;
if (adjustedParamSize > 0) {
return adjustedParamSize;
}
if (waitForLayout && view.isLayoutRequested()) {
return PENDING_SIZE;
}
int adjustedViewSize = viewSize - paddingSize;
if (adjustedViewSize > 0) {
return adjustedViewSize;
}
if (!view.isLayoutRequested() && paramSize == LayoutParams.WRAP_CONTENT) {
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "Glide treats LayoutParams.WRAP_CONTENT as a request for an image the size of"
+ " this device's screen dimensions. If you want to load the original image and are"
+ " ok with the corresponding memory cost and OOMs (depending on the input size), use"
+ " .override(Target.SIZE_ORIGINAL). Otherwise, use LayoutParams.MATCH_PARENT, set"
+ " layout_width and layout_height to fixed dimension, or use .override() with fixed"
+ " dimensions.");
}
return getMaxDisplayLength(view.getContext());
}
return PENDING_SIZE;
}
private boolean isDimensionValid(int size) {
return size > 0 || size == SIZE_ORIGINAL;
}
// Use the maximum to avoid depending on the device's current orientation.
private static int getMaxDisplayLength(@NonNull Context context) {
if (maxDisplayLength == null) {
WindowManager windowManager =
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = Preconditions.checkNotNull(windowManager).getDefaultDisplay();
Point displayDimensions = new Point();
display.getSize(displayDimensions);
maxDisplayLength = Math.max(displayDimensions.x, displayDimensions.y);
}
return maxDisplayLength;
}
整个的计算跟三个参数有关系:
- view的padding值
- LayoutParams里的width(或height)值
- view的getWidth() 或者 getHeight()
如果在布局文件里宽高没有写固定的值,那么LayoutParams里的width(或height)就是-1(MATCH_PARENT) 或 -2(WRAP_CONTENT)。
计算的步骤:
- 先让LayoutParams里的值减去padding的值,大于0,则直接返回
- 是否等待layout之后,是就直接返回
- view的getXX()减去padding的值,大于0,则直接返回
- 计算屏幕大小了~~(以屏幕大小作为照片的大小)
view状态的监听
根据view是否展示,来决定请求是否继续还是暂停。
首先,注册监听:
public final ViewTarget<T, Z> clearOnDetach() {
if (attachStateListener != null) {
return this;
}
attachStateListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
resumeMyRequest();
}
@Override
public void onViewDetachedFromWindow(View v) {
pauseMyRequest();
}
};
maybeAddAttachStateListener();
return this;
}
给view设置tag(为了能取消对应的view上的请求):
@Override
public void setRequest(@Nullable Request request) {
setTag(request);
}
private void setTag(@Nullable Object tag) {
if (tagId == null) {
isTagUsedAtLeastOnce = true;
view.setTag(tag);
} else {
view.setTag(tagId, tag);
}
}
@Nullable
private Object getTag() {
if (tagId == null) {
return view.getTag();
} else {
return view.getTag(tagId);
}
}
在具体的事件下执行网络请求,或者暂停网络:
@SuppressWarnings("WeakerAccess")
@Synthetic void resumeMyRequest() {
Request request = getRequest();
if (request != null && request.isPaused()) {
request.begin();
}
}
@SuppressWarnings("WeakerAccess")
@Synthetic void pauseMyRequest() {
Request request = getRequest();
if (request != null && !request.isCancelled() && !request.isPaused()) {
isClearedByUs = true;
request.pause();
isClearedByUs = false;
}
}
ImageViewTarget
这里主要的点是图片加载中的动画切换。
它有一个Animatable属性,在图片加载的各个周期中触发:
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
setResourceInternal(null);
setDrawable(errorDrawable);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
super.onLoadCleared(placeholder);
if (animatable != null) {
animatable.stop();
}
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);
}
}
ThumbnailImageViewTarget
专门针对小图的的target,主要代码:
@Override
protected void setResource(@Nullable T resource) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
Drawable result = getDrawable(resource);
if (layoutParams != null && layoutParams.width > 0 && layoutParams.height > 0) {
result = new FixedSizeDrawable(result, layoutParams.width, layoutParams.height);
}
view.setImageDrawable(result);
}
protected abstract Drawable getDrawable(T resource);
对于确定大小的Drawable是FixedSizeDrawable源码
SimpleTarget
可以指定大小的Target,这个在开发中应该能不用就尽量不要用。
它有如下问题:
- 必须指定大小,这样glide的内部根据图片展示计算大小的功能就没有了
- 必须主动回收
建议的用法:
Target target =
Glide.with(fragment)
.asBitmap()
.load("http://somefakeurl.com/fakeImage.jpeg")
.apply(fitCenterTransform())
.into(new SimpleTarget (250, 250) {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
// Do something with bitmap here.
}
});
}
// At some later point, clear the Target to release the resources, prevent load queues from
// blowing out proportion, and to improve load times for future requests:
Glide.with(fragment).clear(target);
NotificationTarget & AppWidgetTarget
这2个都是remoteviews上加载图片的时候使用
PreloadTarget
预加载,针对RequestBuilder public Target<TranscodeType> preload(int width, int height)
使用
主要是为了加快显示,先下载下来。