Android ImageGetter images overlapping text
Asked Answered
R

5

24

I'm trying to load a block of HTML into a TextView, including images, using

URLImageParser p = new URLImageParser(articleBody, this);
Spanned htmlSpan = Html.fromHtml(parsedString, p, null);

parsedString is the HTML, by the way. Anyway, it loads up, but the images aren't having any space created for them to sit in, so they end up overlapping the text above them. Here's my URLImageParser file:

public class URLImageParser implements Html.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
        Log.d("height",""+result.getIntrinsicHeight());
        Log.d("width",""+result.getIntrinsicWidth());
        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 {
            URL aURL = new URL(urlString);
            final URLConnection conn = aURL.openConnection(); 
            conn.connect(); 
            final BufferedInputStream bis = new BufferedInputStream(conn.getInputStream()); 
            final Bitmap bm = BitmapFactory.decodeStream(bis);
            Drawable drawable = new BitmapDrawable(bm);
            drawable.setBounds(0,0,bm.getWidth(),bm.getHeight());
            return drawable;
        } catch (Exception e) {
            return null;
        } 
    }
}

}

Any ideas? Thanks a ton.

Redintegrate answered 24/10, 2011 at 0:28 Comment(5)
You are trying to lazy load images? If so, I think this is only problem there. Why not trying to load image and text together at the same time?Tupiguarani
What exactly do you mean by "lazy load"?Redintegrate
Lazy load means show the images as they are downloaded. The alternative being waiting for the images to download and only then display the views.Sessions
I am trying do something similar, can you please tell me what is URLDrawable here ?Nigrescent
@AbhayKumar I think original code including URLDrawable is defined at https://mcmap.net/q/445552/-android-html-imagegetter-as-asynctaskIma
S
-2

Is there a particular reason you need to load it into a text view? Could you just use a WebView instead?

If you can't use Webviews, then the best solution is to not put the images in your text view. Put the images in an ImageView. TextViews don't have any of the layout engine capabilities you need to figure out where to put images and texts in relation to each other. They're not ViewGroups (like LinearLayout or RelativeLayout) and thus have no internal layout specifying capabilities. If you really don't want to use a webview (and all the nice layout engine stuff it has), you're going to have to figure out how to arrange individual TextViews and ImageViews yourself.

Sessions answered 24/10, 2011 at 1:4 Comment(3)
WebViews create substandard experience. I'm trying to write a good native app. I already have my images displaying, I just need them to lay themselves out correctly.Redintegrate
That makes sense. I'll filter out the images and do them separately.Redintegrate
@Redintegrate I think you should select answer below. Because it is solving the problem within textview. No need to use webviews. Webviews are not fır this purpose. And, webviews can be problematic if they are used in listviews.Embolectomy
G
55

You could change your cointainer c (view) to a textView and then make your onPostExecute look like this:

@Override 
protected void onPostExecute(Drawable result) { 
    // set the correct bound according to the result from HTTP call 
    Log.d("height",""+result.getIntrinsicHeight()); 
    Log.d("width",""+result.getIntrinsicWidth()); 
    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();

    // For ICS
    URLImageParser.this.container.setHeight((URLImageParser.this.container.getHeight() 
    + result.getIntrinsicHeight()));

    // Pre ICS
    URLImageParser.this.textView.setEllipsize(null);
} 

This will first draw the image and then immediately set the height of the TextView to the drawable's height + the TextViews height

Grouper answered 18/4, 2012 at 11:35 Comment(9)
This works perfectly! Had the same problem that images were overlapping. With this solution the images are displayed perfectly!Vanessa
container.setText(container.getText()) also works (provided container is a TextView). I wonder, however, what the best way to force a TextView to redo its layout is..Albertina
Oh GOD! I spent about 6 hours to solve this problem until I found your solution. I can sleep nowwwww.Apricot
setHeight doesn't work :( Eclipse tells to use setMinimumHeight, but that doesn't helpAshti
@Kondra007 you need to change cointainer c (view) to a textView as directed by Martin then eclipse won't ask you to use setMinimumHeightKurgan
I was having issues with the container height when using this in a list adapter (and recycling the convertView) - but using container.setText(container.getText()) as pointed out by @DavidCowden seems to fix the problem.Diplomat
As per @Albertina said and @Diplomat comment, I was getting same issue then container.setText(container.getText()) solved my problem.Taskwork
Hi @Martin S I'm still having the same issue with devices having 4.2+ android version. I implemented your bunch of code also but no success, please helpCopulation
as you guyz said container.setText(container.getText()) IS NECESSARY for it to workConatus
M
2

I don't have enough reputation to vote up for Martin S,but his answer is really helpful。And if TextView has displayed a default image before loading,we can change the setHeight() method like this:

    URLImageParser.this.container.setHeight((URLImageParser.this.container.getHeight() 
+ result.getIntrinsicHeight()-mDefaultDrawable.getInstrinsicHeight()));
Mobile answered 6/5, 2014 at 4:42 Comment(0)
M
0

may be we need not change container from View to TextView.

   @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.setMinimumHeight((URLImageParser.this.container.getHeight()+ result.getIntrinsicHeight()));
        URLImageParser.this.container.requestLayout();
        URLImageParser.this.container.invalidate();
    }
Moriahmoriarty answered 22/3, 2013 at 3:3 Comment(0)
C
0

I found a interesting behavior with those solutions: if the loading of an image is to fast and the textview has not been rendered yet (e.g. I use Okhttp with caching, so the second call is quite fast), the textview size is 0.

To resolve this issue, I had converted the ImageGetter back from the AsyncTask and instead start a AsyncTask which creates the Spanned for my TextView and sets the text afterwards.

With this solution it's not required to resize the TextView each time an Image is loaded.

new AsyncTask<TextView, Void, Spanned>() {
      TextView tv;
      @Override
      protected Spanned doInBackground(TextView... params) {
        tv = params[0];
        return Html.fromHtml(feedEntry.getContent(),
                new HttpImageGetter(getActivity(), HttpLoaderImpl.getInstance(getActivity())),
                new Html.TagHandler() {

                  @Override
                  public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
                    //do nothing...
                  }
                });
      }

      @Override
      protected void onPostExecute(final Spanned result) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {

          @Override
          public void run() {
            tv.setText(result);
          }
        });
      }
    }.execute(textView);
Cypro answered 22/8, 2015 at 20:34 Comment(0)
S
-2

Is there a particular reason you need to load it into a text view? Could you just use a WebView instead?

If you can't use Webviews, then the best solution is to not put the images in your text view. Put the images in an ImageView. TextViews don't have any of the layout engine capabilities you need to figure out where to put images and texts in relation to each other. They're not ViewGroups (like LinearLayout or RelativeLayout) and thus have no internal layout specifying capabilities. If you really don't want to use a webview (and all the nice layout engine stuff it has), you're going to have to figure out how to arrange individual TextViews and ImageViews yourself.

Sessions answered 24/10, 2011 at 1:4 Comment(3)
WebViews create substandard experience. I'm trying to write a good native app. I already have my images displaying, I just need them to lay themselves out correctly.Redintegrate
That makes sense. I'll filter out the images and do them separately.Redintegrate
@Redintegrate I think you should select answer below. Because it is solving the problem within textview. No need to use webviews. Webviews are not fır this purpose. And, webviews can be problematic if they are used in listviews.Embolectomy

© 2022 - 2024 — McMap. All rights reserved.