web-dev-qa-db-fra.com

Android HTML ImageGetter en tant que AsyncTask

D'accord, je perds la raison devant celui-ci. J'ai une méthode dans mon programme qui analyse le HTML. Je veux inclure les images en ligne, et j'ai l'impression que l'utilisation de Html.fromHtml (chaîne, Html.ImageGetter, Html.TagHandler) permettra que cela se produise.

Comme Html.ImageGetter n'a pas d'implémentation, c'est à moi d'en écrire une. Cependant, comme l'analyse des URL dans Drawables nécessite un accès réseau, je ne peux pas le faire sur le thread principal. Il doit donc s'agir d'une AsyncTask. Je pense.

Toutefois, lorsque vous passez le paramètre ImageGetter en tant que paramètre à Html.fromHtml, il utilise la méthode getDrawable qui doit être remplacée. Il n'y a donc aucun moyen d'appeler l'ensemble de la transaction ImageGetter.execute qui déclenche la méthode doInBackground. Il est donc impossible de rendre cette opération asynchrone.

Est-ce que j'y vais complètement, ou pire, est-ce impossible? Merci

33
Nick

J'ai fait quelque chose de très similaire (je pense) à ce que vous voulez faire. Ce que j’avais besoin de faire à l’époque est d’analyser le code HTML et de le configurer à nouveau dans TextView. J’avais besoin d’utiliser également Html.ImageGetter et d’avoir le même problème lors de la récupération d’image sur le fil principal.

Les étapes que j'ai faites essentiellement:

  • Créer ma propre sous-classe pour Drawable pour faciliter le redessinage, je l'ai appelé URLDrawable
  • Renvoie la méthode URLDrawable in getDrawable de Html.ImageGetter
  • Une fois que onPostExecute est appelé, je redessine le conteneur du résultat Spanned

Maintenant, le code pour URLDrawable est comme suit


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

Assez simplement, je remplace juste draw pour choisir le Drawable que je place là-bas après la fin de AsyncTask.

La classe suivante est l'implémentation de Html.ImageGetter et celle qui extrait l'image de AsyncTask et met à jour l'image.

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

Enfin, vous trouverez ci-dessous un exemple de programme montrant comment les choses fonctionnent:

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);
100
momo

Pas mal. Cependant, le type DefaultHttpClient est obsolète. Essayez ceci sur la méthode de récupération:

private InputStream fetch(String urlString) throws MalformedURLException, IOException {

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

        return stream;

    }
5
Fred Sousa

Je suis un peu confus: le code HTML que vous souhaitez rendre est-il statique et uniquement destiné à la mise en forme, ou est-il dynamique et provient du Web? Si vous vouliez utiliser ce dernier, c'est-à-dire restituer le code HTML et récupérer les images, eh bien ça va être un peu pénible (suggestion - il suffit d'utiliser une WebView?). 

Quoi qu'il en soit, vous devez d’abord exécuter AsyncTask pour récupérer le code HTML initial. Vous transmettriez ensuite ces résultats à la fonction Html.fromHtml() avec l'implémentation personnalisée de la classe Html.ImageGetter. Ensuite, dans cette implémentation, vous devrez lancer une AsyncTask individuelle pour récupérer chacune des images (vous souhaiterez probablement implémenter une mise en cache). 

Cependant, à la lecture de la documentation (et je pense en avoir vu quelques exemples), il me semblerait que ce n’est pas ce qu’ils désignaient par le Html.ImageGetter. Je pense que cela concerne le code HTML codé en dur avec des références à internal drawables, mais ce n’est que mon point de vue. 

1
dmon

si vous utilisez Picasso, remplacez une partie du code @momo par

/***
         * 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());
        }
1

AsyncTask task = 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();
0
user5881594