Android HTML ImageGetter 作为 AsyncTask

Posted

技术标签:

【中文标题】Android HTML ImageGetter 作为 AsyncTask【英文标题】:Android HTML ImageGetter as AsyncTask 【发布时间】:2011-11-17 11:30:19 【问题描述】:

好的,我正在为这个而失去理智。我的程序中有一个解析 html 的方法。我想包含内联图像,我的印象是使用 Html.fromHtml(string, Html.ImageGetter, Html.TagHandler) 将允许这种情况发生。

由于 Html.ImageGetter 没有实现,由我来编写。但是,由于将URL解析成Drawables需要网络访问,所以我不能在主线程上这样做,所以它必须是一个AsyncTask。我想。

但是,当您将 ImageGetter 作为参数传递给 Html.fromHtml 时,它使用的 getDrawable 方法必须被覆盖。因此无法调用触发 doInBackground 方法的整个 ImageGetter.execute 交易,因此无法真正实现异步。

我是不是完全错了,或者更糟的是,这不可能吗?谢谢

【问题讨论】:

我可能会为此大发雷霆,但为什么不尝试使用自烘焙线程(而不是 asyncTask)来执行此操作。这没有什么可以覆盖的,你可以观察发生的事情。在数据到达后,您需要在主线程中使用处理程序和 Runnable 来调用数据。 @Nick:我很难理解。在什么情况下使用 AsyncTask 是个问题——你认为这其中的哪一部分是行不通的? @MisterSquonk 使用 AsyncTask 是一个问题,因为您必须在对象的实例上调用 execute。但是,要使用 ImageGetter,您将实例作为 Html.fromHtml 函数的参数传递,并且该函数旨在仅调用 ImageGetter 的 getDrawable 方法,该方法将在同一线程上运行,从而导致 NetworkOnMainThreadException 对于与文本重叠的图像,请查看此答案:***.com/a/10208504/1373568(适用于 PRE-ICS,但不适用于 ICS)。 对于使用协程 + Glide 的替代方法,请参阅:***.com/a/58091529/683658 【参考方案1】:

我做了一些与你想做的事情非常相似(我认为)的事情。那时我需要做的是解析 HTML 并将其设置回 TextView,我还需要使用 Html.ImageGetter 并且在主线程上获取图像时遇到同样的问题。

我基本做的步骤:

为Drawable创建自己的子类,方便重绘,我称之为URLDrawable 返回Html.ImageGettergetDrawable方法中的URLDrawable 一旦调用onPostExecute,我就重绘Spanned结果的容器

现在 URLDrawable 的代码如下


public class URLDrawable extends BitmapDrawable 
    // the drawable that you need to set, you could set the initial drawing
    // with the loading image if you need to
    protected Drawable drawable;

    @Override
    public void draw(Canvas canvas) 
        // override the draw to facilitate refresh function later
        if(drawable != null) 
            drawable.draw(canvas);
        
    

很简单,我只是覆盖了draw,所以它会在 AsyncTask 完成后选择我在那里设置的 Drawable。

下面的类是Html.ImageGetter的实现,是从AsyncTask获取图片并更新图片的类

public class URLImageParser implements ImageGetter 
    Context c;
    View container;

    /***
     * Construct the URLImageParser which will execute AsyncTask and refresh the container
     * @param t
     * @param c
     */
    public URLImageParser(View t, Context c) 
        this.c = c;
        this.container = t;
    

    public Drawable getDrawable(String source) 
        URLDrawable urlDrawable = new URLDrawable();

        // get the actual source
        ImageGetterAsyncTask asyncTask = 
            new ImageGetterAsyncTask( urlDrawable);

        asyncTask.execute(source);

        // return reference to URLDrawable where I will change with actual image from
        // the src tag
        return urlDrawable;
    

    public class ImageGetterAsyncTask extends AsyncTask<String, Void, Drawable>  
        URLDrawable urlDrawable;

        public ImageGetterAsyncTask(URLDrawable d) 
            this.urlDrawable = d;
        

        @Override
        protected Drawable doInBackground(String... params) 
            String source = params[0];
            return fetchDrawable(source);
        

        @Override
        protected void onPostExecute(Drawable result) 
            // set the correct bound according to the result from HTTP call
            urlDrawable.setBounds(0, 0, 0 + result.getIntrinsicWidth(), 0 
                    + result.getIntrinsicHeight()); 

            // change the reference of the current drawable to the result
            // from the HTTP call
            urlDrawable.drawable = result;

            // redraw the image by invalidating the container
            URLImageParser.this.container.invalidate();
        

        /***
         * Get the Drawable from URL
         * @param urlString
         * @return
         */
        public Drawable fetchDrawable(String urlString) 
            try 
                InputStream is = fetch(urlString);
                Drawable drawable = Drawable.createFromStream(is, "src");
                drawable.setBounds(0, 0, 0 + drawable.getIntrinsicWidth(), 0 
                        + drawable.getIntrinsicHeight()); 
                return drawable;
             catch (Exception e) 
                return null;
             
        

        private InputStream fetch(String urlString) throws MalformedURLException, IOException 
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpGet request = new HttpGet(urlString);
            HttpResponse response = httpClient.execute(request);
            return response.getEntity().getContent();
        
    

最后,下面是演示工作原理的示例程序:

String html = "Hello " +
"<img src='http://www.gravatar.com/avatar/" + 
"f9dd8b16d54f483f22c0b7a7e3d840f9?s=32&d=identicon&r=PG'/>" +
" This is a test " +
"<img src='http://www.gravatar.com/avatar/a9317e7f0a78bb10a980cadd9dd035c9?s=32&d=identicon&r=PG'/>";

this.textView = (TextView)this.findViewById(R.id.textview);
URLImageParser p = new URLImageParser(textView, this);
Spanned htmlSpan = Html.fromHtml(html, p, null);
textView.setText(htmlSpan);

【讨论】:

你太棒了,这是一个很好的答案,很好的解释,最重要的是它的效果。谢谢一百万! 这很好用!但是有一个问题,当图像很大并且它们上方有文本行时,图像会覆盖它们上方的文本,而不是文本移动以腾出空间。有什么想法吗? 这很好用,但是,invalidate() 还不够 @maxpower47 建议的那样。如果您事先知道图像的大小,您可以简单地将默认可绘制对象设置为足够大并且无效将起作用,但是,如果您在下载图像之前不知道此信息,并且一些较大的图像与您的文本重叠,您必须强制 textview 重新渲染文本,而不仅仅是 redraw() 或 re-requestLayout()。一个简单的解决方案是:TextView t = (TextView) URLImageGetterAsync.this.container; t.setText(t.getText()); 您可以决定是否需要它比这更强大。 图像重叠错误修复在这里可用:***.com/questions/7870312/…(适用于 pre-ICS 和 ICS) 对我不起作用:(空指针异常:" urlDrawable.setBounds(0, 0, 0 + result.getIntrinsicWidth(), 0"【参考方案2】:

相当不错。但是,不推荐使用 DefaultHttpClient 类型。在 fetch 方法上试试这个:

private InputStream fetch(String urlString) throws MalformedURLException, IOException 

        URL url = new URL(urlString);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        InputStream stream = urlConnection.getInputStream();

        return stream;

    

【讨论】:

我遇到了这个问题 ::: The inline style not applied , like style=color:red;"【参考方案3】:

我有点困惑,您要呈现的 HTML 是静态的并且仅用于格式化,还是动态的并且来自网络?如果你想要后者,即渲染 HTML 并检索图像,那会有点痛苦(建议 - 只需使用 WebView?)。

无论如何,您首先必须运行 AsyncTask 来检索初始 HTML。然后,您将使用 Html.ImageGetter 类的自定义实现将这些结果传递给 Html.fromHtml()。然后在那个实现中,你必须启动一个单独的 AsyncTask 来检索每个图像(你可能想要实现一些缓存)。

但是,通过阅读文档(我想我已经看到了一些示例),在我看来这并不是 Html.ImageGetter 的意思。我认为它适用于引用 internal 可绘制对象的硬编码 HTML,但这只是我的看法。

【讨论】:

它来自网络,好吧,我想这是有道理的。我真的不想使用 WebView。我已经写了一个任务,当给定我应用程序不同部分的图像 URL 时,它会返回一个 Drawable,所以也许我可以以某种方式利用它。好痛【参考方案4】:

如果您使用毕加索,请将@momo 代码的一部分更改为

/***
         * Get the Drawable from URL
         * @param urlString
         * @return
         */
        public Drawable fetchDrawable(String urlString) 
            try 
                Drawable drawable = fetch(urlString);
                drawable.setBounds(0, 0, 0 + drawable.getIntrinsicWidth(), 0
                        + drawable.getIntrinsicHeight());
                return drawable;
             catch (Exception e) 
                return null;
            
        

        private Drawable fetch(String urlString) throws MalformedURLException, IOException 
            return new BitmapDrawable(c.getResources(), Picasso.with(c).load(urlString).get());
        

【讨论】:

我遇到了这个问题 ::: The inline style not applied , like style=color:red;"【参考方案5】:

AsyncTask 任务 = new AsyncTask ()

                @Override
                protected String doInBackground(Integer... params) 
                    span = Html.fromHtml(noticeList.get(0)
                            .getContent(), imgGetter, null);
                    return null;
                
                @Override
                protected void onPostExecute(String result) 
                    super.onPostExecute(result);
                    text.setMovementMethod(ScrollingMovementMethod
                            .getInstance());
                    if(span != null)
                        text.setText(span);
                    
                
            ;

            task.execute();

【讨论】:

以上是关于Android HTML ImageGetter 作为 AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章

Android ImageGetter 图像重叠文本

Android EditText载入HTML内容(内容包括网络图片)

Android TextView图文混合编排

Android开发,帖子里的文字和图片怎么处理?

Android - 如何显示来自可绘制资源的图像?

安卓TextView完美展示html格式代码