Отображать изображения на Android с помощью TextView и Html.ImageGetter асинхронно? - PullRequest
3 голосов
/ 21 сентября 2010

Я хочу установить TextView с помощью SpannableString, что из метода ниже:

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

Но ImageGetter здесь необходимо переопределить методом ниже:

public abstract Drawable getDrawable(String source)

Поскольку мне нужно получить файл из Интернета, я должен сделать это асинхронно, и, похоже, это не так.

Как заставить это работать? Спасибо.

Ответы [ 3 ]

14 голосов
/ 17 апреля 2015

Эти ребята отлично поработали, это мое решение с использованием библиотеки Пикассо Square :

//...
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. */ }
    }
}

Мои 2 цента ... Мир!

4 голосов
/ 02 августа 2013

Вот мой код, который захватывает все изображения в строке html (это упрощено от оригинала, поэтому я надеюсь, что это работает):

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);
}

Так что в основном здесь происходит то, что ImageGetter сделает запрос для каждого изображения в описании HTML. Если этого изображения нет в массиве mImageCache и downloadImages имеет значение true, мы запускаем асинхронную задачу для загрузки этого изображения. После его завершения мы добавляем drawable в hashmap, а затем снова вызываем этот метод (но с downloadImages как false, поэтому мы не рискуем бесконечным циклом), где изображение можно будет захватить с вторая попытка.

И для этого вам понадобится класс ImageDownloader, который я использовал:

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());
    }
}
4 голосов
/ 22 сентября 2010

Теперь я использую AsyncTask для загрузки изображений в ImageGetter:

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

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

и снова устанавливаю текст в TextView после загрузки изображения.

Теперь это работает.Но это не удалось, когда я попытался сделать TextView.postInvalidate(), чтобы перерисовать загруженные изображения.Я должен сделать setText() снова в AsyncTask.

Кто-нибудь знает почему?

...