简体   繁体   中英

update UI from AsyncTask

I created a custom TextView to show and images on it. I have to download the images and then show them on the textview . So I thought that I can do my job with a AsyncTask class. On the doInBackground I download and save the image on the Internal storage (for offline mode), and on the onPostExecute I call a method which shows the images on the textview . But it doesn't work. I mean instead of showing the images it shows the img tags.

I also tried to get the status from the AsyncTask class on the onPostExecute method and it's RUNNING . Isn't that weird? I thought that it will be FINISHED . Am I missing something about AsyncTask class?

Below is addImages method, where I find the img tags from the text and store the data on a List , and call the DownLoadImage class which extends the AsyncTask .

 private boolean add(final Context context, final Spannable spannable) {
    path = context.getFilesDir();

    Pattern refImgPattern = Pattern.compile("<img .+?\\/>");
    hasChanges = false;

    refImgMatcher = refImgPattern.matcher(spannable);
    while (refImgMatcher.find()) {

    set = true;
    for (ImageSpan span : spannable.getSpans(refImgMatcher.start(), refImgMatcher.end(), ImageSpan.class)) {
            if (spannable.getSpanStart(span) >= refImgMatcher.start()
                        && spannable.getSpanEnd(span) <= refImgMatcher.end()
                        ) {
                spannable.removeSpan(span);
             } else {
               set = false;
               break;
              }
          }

    String imageUrl = spannable.subSequence(refImgMatcher.start(0), refImgMatcher.end(0)).toString().trim();

    width = 0;
    Pattern widthPattern = Pattern.compile("width=\"[0-9]+?\"");
    Matcher widthMatcher = widthPattern.matcher(imageUrl);

        if (widthMatcher.find()) {
            String w = widthMatcher.group(0);
            w = w.replaceAll("width=", "");
            w = w.replaceAll("\"", "");
            width = Integer.valueOf(w);
        }

            height = 0;
            Pattern heightPattern = Pattern.compile("height=\"[0-9]+?\"");
            Matcher heightMatcher = heightPattern.matcher(imageUrl);

            if (heightMatcher.find()) {
                String h = heightMatcher.group(0);
                h = h.replaceAll("height=", "");
                h = h.replaceAll("\"", "");
                height = Integer.valueOf(h);
            }

     Pattern urlPattern = Pattern.compile("(http|ftp|https):\\/\\/([\\w_-]+(?:(?:\\.[\\w_ -]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])?");
     Matcher urlMatcher = urlPattern.matcher(imageUrl);

        if (urlMatcher.find())
            imageUrl = urlMatcher.group(0);

            imageName = siteData.getId() + "_" + imageUrl.substring(imageUrl.lastIndexOf("/") + 1, imageUrl.length());

            images.add(new Image(imageUrl, imageName, width, height, refImgMatcher.start(0), refImgMatcher.end(0)));


    }

    if (images.size() > 0) {

        for (final Image img : images) {
            image = new File(path, img.name);
            if (!image.exists()) {
            new DownLoadImage(context, spannable, img).execute();
            } else
                addImages(spannable, context, img);
        }
    }

    return hasChanges;
 }

This is the addImages method where I replace the tags with images

 private void addImages(Spannable spannable, Context context, Image im) {
        image = new File(path, im.name);
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bitmap = BitmapFactory.decodeFile(image.getAbsolutePath(), bmOptions);
        if (im.width > 0 && im.height > 0)
            bitmap = Bitmap.createScaledBitmap(bitmap, im.width * 3, im.height * 3, true);

        if (set) {
            hasChanges = true;
            spannable.setSpan(new ImageSpan(context, bitmap),
                    im.startIndex,
                    im.endIndex,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }

    }

and the DownLoadImage class

private class DownLoadImage extends AsyncTask<Void, Void, Void> {

        private Connection connection = Connection.getInstance();
        private Context context;
        private Spannable spannable;
        private Image image;

        public DownLoadImage(Context context, Spannable spannable, Image image) {
            this.spannable = spannable;
            this.context = context;
            this.image = image;
        }

        @Override
        protected Void doInBackground(Void... params) {

            try {
                connection.openConnection(image.path, ConnectionType.GET, false, false, null);

                Integer status = connection.getResponseCode();
                if (status >= 200 && status < 300) {
                    InputStream inputStream = new BufferedInputStream(connection.getInputStream());
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    Actions.saveImage(context, bitmap, image.name);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                connection.closeConnection();
            }


            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            Log.i("status", this.getStatus().toString());
            addImages(spannable, context, image);
        }
    }

***** EDIT *****

the getTextWithImages where I call the add method

private Spannable getTextWithImages(Context context, CharSequence text) {
    images = new ArrayList<>();

    Spannable spannable = spannableFactory.newSpannable(text);
    add(context, spannable);
    return spannable;
}

and the setText method where I call the getTextWithImages

@Override
public void setText(CharSequence text, BufferType type) {
    Spannable s = getTextWithImages(getContext(), text);
    super.setText(s, BufferType.SPANNABLE);
}

You could create an interface that invokes a callback to the UI thread, instead of using the context. For example, in your AsyncTask:

private class DownLoadImage extends AsyncTask<Void, Void, Void> {
    private Connection connection = Connection.getInstance();
    private Context context;
    private Spannable spannable;
    private Image image;
    private OnImageDownloadedListener mOnImageDownloadedListener;
    ...

    @Override
    protected Void doInBackground(Void... params) {
        ...
        ...
    }
    // Interface the task will use to communicate with your activity method.
    public interface OnImageDownloadedListener {
        void onImageDownloaded(Spannable spannable, Image image); // No need for context.
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        if (mOnImageDownloadedListener != null) {
            // If we set a listener, invoke it.
            mOnImageDownloadedListener.onImageDownloaded(spannable, image);
        }
    }

    // Setter.
    public setOnImageDownloadedListener(OnImageDownloadedListener listener) {
        mOnImageDownloadedListener = listener;
    }
}

Then when you create your task try:

if (!image.exists()) {
    // Create the task.
    DownloadImage downloadTask = new DownLoadImage(context, spannable, img);

    // Set your listener.
    downloadTask.setOnImageDownloadedListener(new OnImageLoadedListener() {
        @Override
        public void onImageDownloaded(Spannable spannable, Image image) {
            // Add the images.
            addImages(spannable, **YourContextHere(Activity/etc)**.this, image)
        }
    });

    // Execute.
    downloadTask.execute();
} else
    addImages(spannable, context, img);

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM