如何使Picasso/Glide与Html.ImageGetter一起缓存图像? [英] How to make Picasso/Glide work with Html.ImageGetter for caching images?
问题描述
感谢@Budius所做的所有努力.
我的应用程序的大部分图像工作都可以由Picasso/Glide处理,但是,某些图像在TextView
中由Html.fromHtml
显示.并且TextView
中的图像也经常使用.
Most part of image work of my app could be handled by Picasso/Glide, however, some images are displayed in a TextView
by Html.fromHtml
. And the images in TextView
are also used frequently.
但是,我不知道如何为传递给Html.fromHtml
的ImageGetter
用Picasso/Glide实现getDrawable()
方法.这些图片是否可以在TextView
和其他位图中共享相同的Picasso/Glide缓存?
However, I don't know how to implement getDrawable()
method with Picasso/Glide for the ImageGetter
passed to Html.fromHtml
. Is it possible to share the same cache of Picasso/Glide for these pictures in TextView
and other bitmaps?
还是应该使用自定义的LruCache
代替单独缓存ImageGetter
这些图片?这样会增加OOM错误的风险吗?而且我认为使用两个不同的系统来处理图像会造成不必要的工作负担.
Or should I use an custom LruCache
instead to cache these pictures form ImageGetter
separately? Will this way increase the risk of an OOM error? And I think it creates unnecessary workload to use 2 different systems for processing images.
更新:我尝试使用Picasso的.get()
,但文档显示
Update: I tried to use .get()
of Picasso, but the doc says
/**
* The result of this operation is not cached in memory because the underlying
* {@link Cache} implementation is not guaranteed to be thread-safe.
*/
因此在这种情况下不使用缓存.
So the cache is not used in this case.
更新:
@Budius的答案是正确的,但是缺少为Drawable
设置边界的代码,这使得Drawable
不会显示在TextView
中.所以我将DrawableWrapper
类中的代码修改为:
Update:
The answer of @Budius is right, but code of setting bounds for the Drawable
is missing, which leaves the Drawable
not displayed in the TextView
. So I modified the code in the DrawableWrapper
class into:
public void setWrappedDrawable(Drawable drawable) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = drawable;
if (drawable != null) {
mDrawable.setBounds(0,0,mDrawable.getIntrinsicWidth(),mDrawable.getIntrinsicHeight());
drawable.setCallback(this);
}
}
更新:该问题仍未解决.如果实施上述解决方案,则TextView
中的图像会有一些奇怪的行为.有时,除非您切换到另一个应用程序然后再切换回去,否则无法刷新图像,并且图像的位置严重不正确.
Update:, the problem is still unsolved. If you implement the solution forementioned, there are some strange behaviors for the image in TextView
. Sometimes the image could not be refreshed unless you switch to another app and switch back, and the position of image is severely incorrect.
更新:我已在下面发布了所有用于测试的代码.仍然有一些错误.没有占位符,它仍然会引发NPE.使用占位符,其行为非常奇怪.第一次输入TestActivity
时,它会显示占位符,但不会更改为下载的图片.但是,当我切换到另一个应用程序或按返回按钮并再次输入TestActivity
后,将显示图片(也许是因为它在缓存中?).
Update: I have post all the code for test below. There're still some bugs. Without a placeholder, it still throws an NPE. With a placeholder, the behavior is very strange. The first time I enter TestActivity
, it shows the placeholder but it won't change into the downloaded pic. But after I switch to another app or press a back button and enter TestActivity
again, the pic is displayed(maybe because it's in the cache?).
图片的大小也是正确的,但是图像的位置仍然没有.如果我呼叫mDrawable.setBounds(getBounds());
而不是mDrawable.setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
,它将不会显示.
And also the size of pic is right but the place is still not left for the image. And if I call mDrawable.setBounds(getBounds());
instead of mDrawable.setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
, it will not be displayed.
public class DrawableWrapper extends Drawable implements Drawable.Callback {
private Drawable mDrawable;
public DrawableWrapper(Drawable drawable) {
setWrappedDrawable(drawable);
}
@Override
public void draw(Canvas canvas) {
mDrawable.draw(canvas);
}
@Override
public int getIntrinsicWidth() {
return 384;
}
@Override
public int getIntrinsicHeight() {
return 216;
}
//... other delegation methods are omitted
public void setWrappedDrawable(Drawable drawable) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = drawable;
if (drawable != null) {
mDrawable.setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
drawable.setCallback(this);
}
}
}
PicassoTargetDrawable
public class PicassoTargetDrawable extends DrawableWrapper
implements Target {
private Context context;
public PicassoTargetDrawable(Context context) {
super(new ColorDrawable(0));
// use application context to not leak activity
this.context = context.getApplicationContext();
}
public void onBitmapFailed(Drawable errorDrawable) {
setWrappedDrawable(errorDrawable);
invalidateSelf();
}
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setWrappedDrawable(new BitmapDrawable(context.getResources(), bitmap));
context = null;
invalidateSelf();
}
public void onPrepareLoad(Drawable placeHolderDrawable) {
setWrappedDrawable(placeHolderDrawable);
invalidateSelf();
}
}
TestActivity
public class TestActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
setContentView(textView);
String html = "<div>test<br/>" +
"<img src=\"http://i2.cdn.turner.com/money/dam/assets/150910165544-elon-evo-open-still-384x216.png\"></img>" +
"<br/>/test</div>";
textView.setText(Html.fromHtml(html, new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
PicassoTargetDrawable d = new PicassoTargetDrawable(TestActivity.this);
Picasso.with(TestActivity.this)
.load(source)
//add placeholder here
.into(d);
return d;
}
}, null));
}
}
推荐答案
我的建议是返回可绘制的环绕图.并继续使用Picasso下载图像.
My Suggestion is to return a wrap drawable. And keep using Picasso to download the image.
在下面的链接上,您可以找到一个DrawableWrapper,它来自Google的支持库,但它不是公共文档的一部分,因此我将整个代码复制到您的项目中
On the following link you can find an DrawableWrapper, it's from Googles support library, but it's not part of the public docs, so I would just copy the whole code into your project https://android.googlesource.com/platform/frameworks/support/+/master/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
然后从中创建一个PicassoTargetDrawable
.
public class PicassoTargetDrawable extends DrawableWrapper
implements Picasso.Target {
private Context context;
public PicassoTargetDrawable(Context context) {
super(new ColorDrawable(0));
// use application context to not leak activity
this.context = context.getApplicationContext();
}
public void onBitmapFailed(Drawable errorDrawable) {
setWrappedDrawable(errorDrawable);
invalidateSelf();
}
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setWrappedDrawable(new BitmapDrawable(context.getResources(), bitmap));
context = null;
invalidateSelf();
}
public void onPrepareLoad(Drawable placeHolderDrawable) {
setWrappedDrawable(placeHolderDrawable);
invalidateSelf();
}
}
然后只需将其加载
public void Drawable getDrawable(String source) {
PicassoTargetDrawable d = new PicassoTargetDrawable(context);
Picasso.with(context)
.load(source)
..... add here onError and placeholder drawables
.into(d);
return d;
}
PS. 我在写这些文章时并没有花太多的时间,可能会有一些错别字和一些问题需要整理,但是您一定足以理解这个概念.
PS.: I wrote all this without looking up too much, there will probably be a few typos and a few issues to sort it out, but it's certainly enough for you to understand the concept.
更新: 只需更正您的代码即可.
update: Just correcting your code.
TextView已经告诉WrapDrawable它应该使用的边界.如果您要告诉新的mDrawable它可以使用所需的任何大小,则它将使用所需的任何大小.因此,您应该传递给WrapDrawable的尺寸,而不是传递其自身的固有宽度/高度
The TextView already told the WrapDrawable the Bounds it should use. If you're telling the new mDrawable that it can use whatever size it wants, it will use whatever size it wants. So instead of passing its own intrinsic width/height, you should pass the size that was give to the WrapDrawable
public void setWrappedDrawable(Drawable drawable) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = drawable;
if (drawable != null) {
mDrawable.setBounds(getBounds());
drawable.setCallback(this);
}
}
这篇关于如何使Picasso/Glide与Html.ImageGetter一起缓存图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!