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

前端 未结 3 1034
孤独总比滥情好
孤独总比滥情好 2020-12-25 09:25

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

Html.fromHtml(String source, Html.ImageGetter imageGe         


        
相关标签:
3条回答
  • 2020-12-25 09:43

    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?

    0 讨论(0)
  • 2020-12-25 09:44

    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 讨论(0)
  • 2020-12-25 09:56

    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!

    0 讨论(0)
提交回复
热议问题