在 Android 上使用 TextView 和 Html.ImageGetter 异步显示图像?
Posted
技术标签:
【中文标题】在 Android 上使用 TextView 和 Html.ImageGetter 异步显示图像?【英文标题】:Display images on Android using TextView and Html.ImageGetter asynchronously? 【发布时间】:2011-04-15 02:02:16 【问题描述】:我想通过以下方法设置TextView
和SpannableString
:
html.fromHtml(String source, Html.ImageGetter imageGetter,
Html.TagHandler tagHandler)
但是这里的ImageGetter
需要覆盖下面的方法:
public abstract Drawable getDrawable(String source)
因为我需要从互联网上获取可绘制对象,所以我必须异步进行,似乎不是。
如何让它发挥作用? 谢谢。
【问题讨论】:
【参考方案1】:这些人做得很好,这是我使用 Square 的 Picasso 库的解决方案:
//...
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 美分...和平!
【讨论】:
是的!复制、粘贴、享受:D 请问这里为什么用LevelListDrawable
@cass_? addLevel
和 setLevel
是关于什么的?【参考方案2】:
这是我的代码,它抓取了 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 为真,我们将运行异步任务来下载该图像。完成后,我们将可绘制对象添加到 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());
【讨论】:
嗨,我的想法和你的想法一样。图片下载完成后需要重新setText。 感谢此代码-sn-p。如果你想覆盖多个屏幕尺寸,你可以使用 DisplayMetrics 类来有效地设置边界。【参考方案3】:现在我正在使用 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()
重绘下载的图像时它失败了。我必须在AsyncTask
中再次执行setText()
。
有人知道为什么吗?
【讨论】:
您好,我刚刚使用跨区内容再次设置了文本。它将再次触发 ImageGetter。 我说的是 ImageDownloadAsyncTask(textView, htmlString, source) 为什么需要 htmlString 作为参数? 因为我需要再次将 Spanned htmlString 设置为 TextView。它也可以保存在某个地方,并在图像下载后获取。 @shiami 我在图像上设置了 onClickListener 以通过 ViewPager 显示 FullScreenView 但我想要滑动功能,以便我可以滑动该文本视图中的所有图像。有可能吗?以上是关于在 Android 上使用 TextView 和 Html.ImageGetter 异步显示图像?的主要内容,如果未能解决你的问题,请参考以下文章
在方向更改上保存 TextView 的设置 - Android?