如何使Picasso/Glide与Html.ImageGetter一起缓存图像? [英] How to make Picasso/Glide work with Html.ImageGetter for caching images?

查看:205
本文介绍了如何使Picasso/Glide与Html.ImageGetter一起缓存图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

感谢@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.fromHtmlImageGetter用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆