问题
Thanks for all the effort @Budius has made.
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.
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?
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.
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.
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);
}
}
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.
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?).
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.
DrawableWrapper
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));
}
}
回答1:
My Suggestion is to return a wrap drawable. And keep using Picasso to download the image.
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
And then you create a PicassoTargetDrawable
from it.
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();
}
}
Then it's just a matter of loading it up
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.: 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.
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);
}
}
来源:https://stackoverflow.com/questions/33040002/how-to-make-picasso-glide-work-with-html-imagegetter-for-caching-images