关于Target
在Glide中,Target 是介于请求和请求者之间的中介者的角色。Target 负责展示占位符,加载资源,并为每个请求决定合适的尺寸。被使用得最频繁的是 ImageViewTargets ,它用于在 ImageView 上展示占位符、Drawable 和 Bitmap 。用户还可以实现自己的 Target ,或者从任何可用的基类派生子类。
指定目标
into(Target) 方法不仅仅用于启动每个请求,它同时也指定了接收请求结果的 Target 。Glide 提供了一个辅助方法 into(ImageView) ,它接受一个 ImageView
参数并为请求的资源类型指定了合适的 Target
。
为了让使用自定义的 Target 更方便,into()
方法返回了提供给它的参数:
Target<Bitmap> target = Glide.with(fragment) .asBitmap() .load(url) .into(imageView);...// Some time later:Glide.with(fragment).clear(target);
目标和自动取消
Target可以(并且通常应当)被复用于会在同一个地方展示的一连串加载过程。 复用 Target 允许 Glide 在新的加载开始时自动取消或复用旧的加载过程的资源。 Target 复用失败可能导致旧的请求资源替换掉稍新请求的资源。
定制目标
复用自定义 Target 的一个简单方案,保持它们的实例引用就行了:
private class WidgetHolder { private final Fragment fragment; private final Target<Widget> widgetTarget; public WidgetHolder(Fragment fragment, Widget widget) { this.fragment = fragment; widgetTarget = new CustomWidgetTarget(widget); } public void showInWidget(Uri uri) { Glide.with(fragment) .load(uri) .into(widgetTarget); }}
Glide可以使用 getRequest()
和 setRequest()
方法来查找 Target ,或取消 Target 上的请求。这意味着所有的自定义 Target 都必须实现这些方法。最简单的办法莫过于直接继承 BaseTarget
.
View目标
一些自定义的Target能为 getRequest()
和 setRequest()
方法提供更为智能的实现,以避免复用 Target 的一些严格要求。例如 ViewTarget,它利用了Android 框架的 getTag() 和 setTag() 方法来完成这些工作。
@Overridepublic Request getRequest() { return (Request) view.getTag();}@Overridepublic void setRequest(Request request) { view.setTag(request);}
因为 tag 是 View 的属性,所以属于新一次请求的 ViewTarget 可以在很容易地从之前的 ViewTarget 中找出并取消/复用之前的请求。
这样做的一个好处是,当你使用 into(ImageView)
向view中加载图片时,或你从 ViewTarget
派生出了新的 Target 时,你可以为每次加载都传入一个新的 Target:
@Overridepublic void onBindViewHolder(ViewHolder vh, int position) { int resourceId = resourceIds.get(position) Glide.with(fragment) .load(resourceId) .into(new CustomViewTarget(vh.imageView));
尺寸测量
默认情况下,Glide 使用 Target 通过 getSize()
方法提供的尺寸作为请求的目标尺寸。这种设计允许 Glide 可以选取合适的 url ,降低采样率,剪裁和转换图像,从而最小化内存的使用,并确保加载尽可能快。
最简单的实现getSize()
的方法可能是直接调用库提供的回调:
定制目标
@Overridepublic void getSize(SizeReadyCallback cb) { cb.onSizeReady(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);}
你也可以向你的 Target 的构造函数传入一个尺寸,并在这个回调中提供这个尺寸:
public class CustomTarget<T> implements Target<T> { private final int width; private final int height; public CustomTarget(int width, int height) { this.width = width; this.height = height; } ... @Override public void getSize(SizeReadyCallback cb) { cb.onSizeReady(width, height); }}
View目标
ViewTarget 通过检查 View 的属性和/或使用一个 OnPreDrawListener
在 View 绘制之前直接测量尺寸来实现 getSize()
方法。我们使用以下逻辑:
如果 View 的的宽或高被设置为 > 0,则使用这个尺寸
如果 View 的宽或高被设置为 WRAP_CONTENT ,则使用屏幕的宽或高
如果仍然至少有一个维度 <= 0 并且不是 WRAP_CONTENT ,添加一个 OnPreDrawListener 监听布局。
WRAP_CONTENT
请注意,Glide 可能对 WRAP_CONTENT 处理不是特别好。这是因为我们很难知道用户的实际意图,尤其是还请求了变换(Transformations
)的时候。
我们可以将 WRAP_CONTENT 视为用户请求原始的未修改的图像,但这么做会有在加载超大图像时导致 OutOfMemoryException 的风险。另外,尤其是因为View通常不会超出屏幕,Android Framework 最后可能会将我们设法成功加载的任意高分辨率图像给缩小(downscaling)。
使用屏幕尺寸,至少允许我们缩小超大的图片,而又不完全忽略用户的原始请求意图。
尺寸与性能
一般来说,在加载图片的 View 被设置了显式的dp尺寸的情况下,Glide 提供了最快、最可预测的结果。在不可能获得明确的 dp 尺寸时,Glide 也通过使用 OnPreDrawListeners
为 layout weight
, MATCH_PARENT
以及其他相对尺寸提供了相当鲁棒的支持。如果这些都不奏效,Glide 也为 WRAP_CONTENT
提供了应该较为合理的行为。
其他情况
在任何情况下,如果 Glide 看起来获取了错误的 View 尺寸,你都可以手动覆盖来纠正它。你可以选择扩展 ViewTarget
实现你自己的逻辑,或者使用 RequestOption
里的方法。
共同学习,写下你的评论
评论加载中...
作者其他优质文章