开发者

Display images on Android using TextView and Html.ImageGetter asynchronously?

开发者 https://www.devze.com 2023-01-16 19:38 出处:网络
I want to set a TextView with SpannableString which is from the method below: Html.fromHtml(String source, Html.ImageGetter imageGetter,

I want to set a TextView with SpannableString which is from the method below:

Html.fromHtml(String source, Html.ImageGetter imageGetter, 
   Html.TagHandler tagHandler)

But the ImageGetter here need开发者_如何学C to override the method below:

public abstract Drawable getDrawable(String source)

Because I need to get the drawable from the internet, I have to do it asynchronously and seems it is not.

How to make it work? Thanks.


These guys did a great job, this is my solution using Square's Picasso library:

//...
final TextView textView = (TextView) findViewById(R.id.description);
        Spanned spanned = Html.fromHtml(getIntent().getStringExtra(EXTRA_DESCRIPTION),
                new Html.ImageGetter() {
                    @Override
                    public Drawable getDrawable(String source) {
                        LevelListDrawable d = new LevelListDrawable();
                        Drawable empty = getResources().getDrawable(R.drawable.abc_btn_check_material);;
                        d.addLevel(0, 0, empty);
                        d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight());
                        new ImageGetterAsyncTask(DetailActivity.this, source, d).execute(textView);

                        return d;
                    }
                }, null);
        textView.setText(spanned);
//...


class ImageGetterAsyncTask extends AsyncTask<TextView, Void, Bitmap> {


    private LevelListDrawable levelListDrawable;
    private Context context;
    private String source;
    private TextView t;

    public ImageGetterAsyncTask(Context context, String source, LevelListDrawable levelListDrawable) {
        this.context = context;
        this.source = source;
        this.levelListDrawable = levelListDrawable;
    }

    @Override
    protected Bitmap doInBackground(TextView... params) {
        t = params[0];
        try {
            Log.d(LOG_CAT, "Downloading the image from: " + source);
            return Picasso.with(context).load(source).get();
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    protected void onPostExecute(final Bitmap bitmap) {
        try {
            Drawable d = new BitmapDrawable(context.getResources(), bitmap);
            Point size = new Point();
            ((Activity) context).getWindowManager().getDefaultDisplay().getSize(size);
            // Lets calculate the ratio according to the screen width in px
            int multiplier = size.x / bitmap.getWidth();
            Log.d(LOG_CAT, "multiplier: " + multiplier);
            levelListDrawable.addLevel(1, 1, d);
            // Set bounds width  and height according to the bitmap resized size
            levelListDrawable.setBounds(0, 0, bitmap.getWidth() * multiplier, bitmap.getHeight() * multiplier);
            levelListDrawable.setLevel(1);
            t.setText(t.getText()); // invalidate() doesn't work correctly...
        } catch (Exception e) { /* Like a null bitmap, etc. */ }
    }
}

My 2 cents... Peace!


Now I'm using an AsyncTask to download the images in the ImageGetter:

Spanned spannedContent = Html.fromHtml(htmlString, new ImageGetter() {

        @Override
        public Drawable getDrawable(String source) {
            new ImageDownloadAsyncTask().execute(textView, htmlString, source);
            return null;
        }
    }, null);

And set the text again into the TextView when the image has been downloaded.

Now it works. But It failed when I tried to do the TextView.postInvalidate() to redraw the downloaded images. I have to do setText() again in the AsyncTask.

Does anyone know why?


Here's my code that grabs all images in the html string (it's simplified from the original, so I hope it works):

private HashMap<String, Drawable> mImageCache = new HashMap<String, Drawable>();
private String mDescription = "...your html here...";

private void updateImages(final boolean downloadImages) {
    if (mDescription == null) return;
    Spanned spanned = Html.fromHtml(mDescription,
        new Html.ImageGetter() {
        @Override
        public Drawable getDrawable(final String source) {
            Drawable drawable = mImageCache.get(source);
            if (drawable != null) {
                return drawable;
            } else if (downloadImages) {
                new ImageDownloader(new ImageDownloader.ImageDownloadListener() {
                    @Override
                    public void onImageDownloadComplete(byte[] bitmapData) {
                        Drawable drawable = new BitmapDrawable(getResources(),
                                BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length));
                        try {
                            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
                        } catch (Exception ex) {}
                        mImageCache.put(source, drawable);
                        updateImages(false);
                    }
                    @Override
                    public void onImageDownloadFailed(Exception ex) {}
                }).execute(source);
            }
            return null;
        }
    }, null);
    tvDescription.setText(spanned);
}

So basically what happens here is the ImageGetter will make a request for every image in the html description. If that image isn't in the mImageCache array and downloadImages is true, we run an async task to download that image. Once it has completed, we add the drawable into the hashmap, and then make a call to this method again (but with downloadImages as false so we don't risk an infinite loop), where the image will be able to be grabbed with the second attempt.

And with that, you'll need the ImageDownloader class that I used:

public class ImageDownloader extends AsyncTask {
    public interface ImageDownloadListener {
        public void onImageDownloadComplete(byte[] bitmapData);
        public void onImageDownloadFailed(Exception ex);
    }

    private ImageDownloadListener mListener = null;

    public ImageDownloader(ImageDownloadListener listener) {
        mListener = listener;
    }

    protected Object doInBackground(Object... urls) {
        String url = (String)urls[0];
        ByteArrayOutputStream baos = null;
        InputStream mIn = null;
        try {
            mIn = new java.net.URL(url).openStream();
            int bytesRead;
            byte[] buffer = new byte[64];
            baos = new ByteArrayOutputStream();
            while ((bytesRead = mIn.read(buffer)) > 0) {
                if (isCancelled()) return null;
                baos.write(buffer, 0, bytesRead);
            }
            return new AsyncTaskResult<byte[]>(baos.toByteArray());

        } catch (Exception ex) {
            return new AsyncTaskResult<byte[]>(ex);
        }
        finally {
            Quick.close(mIn);
            Quick.close(baos);
        }
    }

    protected void onPostExecute(Object objResult) {
        AsyncTaskResult<byte[]> result = (AsyncTaskResult<byte[]>)objResult;
        if (isCancelled() || result == null) return;
        if (result.getError() != null) {
            mListener.onImageDownloadFailed(result.getError());
        }
        else if (mListener != null)
            mListener.onImageDownloadComplete(result.getResult());
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消