[TUT] ImageView with Loading Spinner

By blundell  

Hi guys, read a post where someone wanted to load a url image in an ImageView and show a loading spinner whilst this was happening. So I thought it would be a fun exercise for me to do.

I originally posted this here: AndDev but have now migrated to my own blog.

Ok so i’ve implemented a View that shows a loading spinner whilst it is retrieving an image from the web.
This is useful when people have slow http connections and you at least want to show them that your loading something!

To start with you will need:

     <uses-permission android:name="android.permission.INTERNET" />

in your manifest.

Ok this is the ImageView Object, it is mostly self explanatory, just add it to your project package:

import java.io.IOException;
import java.net.MalformedURLException;
 
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.Handler.Callback;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
 
/**
 * Free for anyone to use, just say thanks and share
 * @author Blundell
 *
 */
public class LoaderImageView extends LinearLayout{
 
        private static final int COMPLETE = 0;
        private static final int FAILED = 1;
 
        private Context mContext;
        private Drawable mDrawable;
        private ProgressBar mSpinner;
        private ImageView mImage;
       
        /**
         * This is used when creating the view in XML
         * To have an image load in XML use the tag 'image="http://developer.android.com/images/dialog_buttons.png"'
         * Replacing the url with your desired image
         * Once you have instantiated the XML view you can call
         * setImageDrawable(url) to change the image
         * @param context
         * @param attrSet
         */
        public LoaderImageView(final Context context, final AttributeSet attrSet) {
                super(context, attrSet);
                final String url = attrSet.getAttributeValue(null, "image");
                if(url != null){
                        instantiate(context, url);
                } else {
                        instantiate(context, null);
                }
        }
       
        /**
         * This is used when creating the view programatically
         * Once you have instantiated the view you can call
         * setImageDrawable(url) to change the image
         * @param context the Activity context
         * @param imageUrl the Image URL you wish to load
         */
        public LoaderImageView(final Context context, final String imageUrl) {
                super(context);
                instantiate(context, imageUrl);        
        }
 
        /**
         *  First time loading of the LoaderImageView
         *  Sets up the LayoutParams of the view, you can change these to
         *  get the required effects you want
         */
        private void instantiate(final Context context, final String imageUrl) {
                mContext = context;
               
                mImage = new ImageView(mContext);
                mImage.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
               
                mSpinner = new ProgressBar(mContext);
                mSpinner.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                       
                mSpinner.setIndeterminate(true);
               
                addView(mSpinner);
                addView(mImage);
               
                if(imageUrl != null){
                        setImageDrawable(imageUrl);
                }
        }
 
        /**
         * Set's the view's drawable, this uses the internet to retrieve the image
         * don't forget to add the correct permissions to your manifest
         * @param imageUrl the url of the image you wish to load
         */
        public void setImageDrawable(final String imageUrl) {
                mDrawable = null;
                mSpinner.setVisibility(View.VISIBLE);
                mImage.setVisibility(View.GONE);
                new Thread(){
                        public void run() {
                                try {
                                        mDrawable = getDrawableFromUrl(imageUrl);
                                        imageLoadedHandler.sendEmptyMessage(COMPLETE);
                                } catch (MalformedURLException e) {
                                        imageLoadedHandler.sendEmptyMessage(FAILED);
                                } catch (IOException e) {
                                        imageLoadedHandler.sendEmptyMessage(FAILED);
                                }
                        };
                }.start();
        }
       
        /**
         * Callback that is received once the image has been downloaded
         */
        private final Handler imageLoadedHandler = new Handler(new Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                        switch (msg.what) {
                        case COMPLETE:
                                mImage.setImageDrawable(mDrawable);
                                mImage.setVisibility(View.VISIBLE);
                                mSpinner.setVisibility(View.GONE);
                                break;
                        case FAILED:
                        default:
                                // Could change image here to a 'failed' image
                                // otherwise will just keep on spinning
                                break;
                        }
                        return true;
                }              
        });
 
        /**
         * Pass in an image url to get a drawable object
         * @return a drawable object
         * @throws IOException
         * @throws MalformedURLException
         */
        private static Drawable getDrawableFromUrl(final String url) throws IOException, MalformedURLException {
                return Drawable.createFromStream(((java.io.InputStream)new java.net.URL(url).getContent()), "name");
        }
       
}

An example of instantiation within XML would look like this, changing my package name to wherever you have put the LoaderImageView class file:

<com.blundell.tut.LoaderImageView
   android:id="@+id/loaderImageView"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   image="http://developer.android.com/images/dialog_buttons.png"
   />

And an example of programmatic instantiation looks like this:

final LoaderImageView image = new LoaderImageView(this, "http://developer.android.com/images/dialog_buttons.png");
        image.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

I’ve also attached the example project, which has both types of ImageView class’s so refer to that if you need a bit more background.

Remember to say thanks :-) and any questions just ask!

—-> Download Image Loader Src Here <----


35 Comments

  1. Posted April 12, 2014 at 2:19 am | Permalink | Reply

    Thanks

  2. Ibrahim
    Posted March 23, 2014 at 9:14 am | Permalink | Reply

    This very nice thanks a lot, but I have a question.

    How can I implement this to a sliding Menu app. If you can help me it will be very helpful and nice.

    thanks a lot

  3. yoga
    Posted March 19, 2014 at 8:21 am | Permalink | Reply

    Hi,

    I have a requirement to load the image from an https site and this works only for http sites. any idea what needs to be changed/updated in the code? or could you please update and send me the code.

  4. Posted October 4, 2013 at 12:39 pm | Permalink | Reply

    Very nice dude…

    i have also found one good link here….
    Download Images From Web And Lazy Load In ListView

  5. Dinesh
    Posted April 27, 2013 at 11:06 pm | Permalink | Reply

    Hi…The image does not load…the progressbar is loading loading keep loading. Am I missing anything?

    • blundell
      Posted April 28, 2013 at 3:48 pm | Permalink | Reply

      Yes. Do you have the internet permission in your manifest? Check your logcat for errors

      • Dinesh
        Posted April 29, 2013 at 7:10 am | Permalink | Reply

        Yes I have Internet permission in manifest file.In logcat there is no errors. I have to load the image via code not in xml. I have set width and height in XML for that ImageView. Shoud I set layout Layout params in code too?

      • Dinesh
        Posted April 29, 2013 at 7:33 am | Permalink | Reply

        I have tried your example project. In that too image does not load, progress bar is loading loading … :(

  6. Max
    Posted February 18, 2013 at 7:23 pm | Permalink | Reply

    Hey there, a lot of thanks to you! Your class is just awesome!

    But I’ve got 1 problem:
    My Layout is defined in XML, and I want the downloaded Image matching the width of the screen.

    In my Relative Layout (with fill_parent attribute) I define it like this:

    In the Activity I call it this way:
    image = (LoaderImageView) findViewById(R.id.loaderImageView);
    (And update it: image.setImageDrawable(getAnImageUrl());)

    Now the images are resized to a smaller size and are displayed aligning the left side of the screen.

    Would you please tell me the way out of this, so that the Image (-width) fills the screen? :)

    Thank you a lot in advance!
    Greetings from Germany,
    Max

    • blundell
      Posted February 18, 2013 at 7:59 pm | Permalink | Reply

      wow thanks! Hmm do you need android:scaleType=”fitXY” in your layout?

      • Max
        Posted February 19, 2013 at 5:28 pm | Permalink | Reply

        Thx for your fast reply.
        Didn’t used scaleType but by adding it it doesn’t change anything.

        android:id=”@+id/loaderImageView”
        android:layout_width=”match_parent”
        android:layout_height=”wrap_content”
        android:scaleType=”fitXY”
        android:layout_centerHorizontal=”true”
        android:layout_centerVertical=”true”
        image=”http:/…”

      • Max
        Posted February 25, 2013 at 12:34 pm | Permalink | Reply

        I found something:
        When I change minSdk from 4 to 3 the Image fills the screen, BUT it looks like the whole layout gets scaled (Action Bar is a lot to big, buttons seem to jump out of the screen, Option-items are to big for the screen and only readable halfly, …)

        Do you have any idea what the problem is?

        Cheers,
        Max

    • Posted May 27, 2014 at 9:44 am | Permalink | Reply

      Hi Max,

      I had the same problem. I ended up changing the ImageView parameters in code in the LoaderImageView class.
      Here is the compete function I changed.

      private void instantiate(final Context context, final String imageUrl) {
      mContext = context;

      mImage = new ImageView(mContext);
      mImage.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
      mImage.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
      mImage.setAdjustViewBounds(true);

      mSpinner = new ProgressBar(mContext);
      mSpinner.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));

      mSpinner.setIndeterminate(true);

      addView(mSpinner);
      addView(mImage);

      if(imageUrl != null){
      setImageDrawable(imageUrl);
      }
      }

  7. Mr_ZLO
    Posted February 6, 2013 at 11:30 am | Permalink | Reply

    thanks!)

  8. Posted November 5, 2012 at 5:55 am | Permalink | Reply

    Perfect work Sir, Its working fine but gives me an Android AVD Error, “No compatible targets were found. Do you wish to add a new Android Virtual Device?”

    • blundell
      Posted November 5, 2012 at 9:53 am | Permalink | Reply

      So if you have a phone plugged in, ADT isn’t recognising it, either use an Emulator (AndroidAVD) or google and download the drivers for that device

  9. Posted November 5, 2012 at 5:33 am | Permalink | Reply

    Thank You Sir, Its helpful.

  10. Hicham Taoufikallah
    Posted September 2, 2012 at 1:47 pm | Permalink | Reply

    Hello, just want to say thank you :)

  11. dilip
    Posted August 8, 2012 at 10:08 am | Permalink | Reply

    i have used ur tutorial ,end almost everything’s look fine,but image does not display,i know that ur code is perfect but something is wrong either with my eclipse or my AVD ,i have given the user permission for internet in manifest file,and set the proxy address in my avd so browser is run on emulator ,but noe i have stuck ,any webservices does not run because this issue,
    can u help?

    • blundell
      Posted August 9, 2012 at 7:01 am | Permalink | Reply

      IF you are behind a proxy sometimes you’ll have to add that proxy to the HttpClient you are using for web requests. Why don’t you try your code on a real device over 3G that will avoid the issue.

  12. Philip
    Posted July 12, 2012 at 5:43 pm | Permalink | Reply

    Hi, I am trying to experiment with your code, but I am getting errors just trying to run it. one of the errors is
    “button cannot be resolved or is not a field”. I find this error in the ImageExampleXML.java file. Another error in the same place is the main cannot be resolved or is not a field? what does this mean? Thank you for your time

    • blundell
      Posted July 13, 2012 at 8:06 am | Permalink | Reply

      Hi this means your R.java file hasn’t been generated. Ensure you have ‘build automatically’ turned on and there are no error’s in your project especially in the XML.

      • Philip
        Posted July 13, 2012 at 7:27 pm | Permalink | Reply

        i realized what i did wrong. thanks!

  13. erez
    Posted June 29, 2012 at 3:13 pm | Permalink | Reply

    I’v used your code and it’s awsome!!
    One question:
    How can I set the size of the image to FILL_PARENT?

    Thanks

    • blundell
      Posted June 29, 2012 at 4:30 pm | Permalink | Reply

      No worries! Yeah either do it in the XML or when you set the LayoutParams

  14. guest
    Posted June 26, 2012 at 7:29 am | Permalink | Reply

    Thanks for your code

  15. Posted June 16, 2012 at 5:58 am | Permalink | Reply

    Hi, thanks a lot for your code snippets. It helped me quite a lot.

    I am wondering now how I should eventually adapt it for the following:

    http://developer.android.com/training/displaying-bitmaps/index.html

    to avoid ‘java.lang.OutofMemoryError: bitmap size exceeds VM budget’

    Should I do it maybe in :
    getDrawableFromUrl(final String url) maybe ??

    • blundell
      Posted June 16, 2012 at 3:10 pm | Permalink | Reply

      Yes perhaps wrap the getDrawableFromUrl with another bitmap creation that takes a BitMap Options value.

  16. rlf, Psychotherapeut Wien
    Posted May 13, 2012 at 9:39 am | Permalink | Reply

    Looks great! How can I use this to *update* an ImageView after a thread pulling data from a database has been completed (in the onPostExecute() method)?

    kind regards,
    r.

    • blundell
      Posted May 14, 2012 at 6:33 pm | Permalink | Reply

      Hmm you could pass the ImageView to the ASyncTask but be careful because you would be passing a context to your whole Activity. I would just create an interface that the ASyncTask could call when it is finished and let your Activity worry about updating the ImageView, that way ut keeps a nice degree of separation

      • rlf
        Posted May 14, 2012 at 6:57 pm | Permalink | Reply

        thanks for your feedback, I will try that! :)

  17. Raul
    Posted April 27, 2012 at 10:57 pm | Permalink | Reply

    Thanks! I mean it! Thank you very much!

  18. alejandroSoljancic
    Posted April 17, 2012 at 2:52 pm | Permalink | Reply

    excellent post!!! thanks!!

  19. Posted March 11, 2012 at 7:21 pm | Permalink | Reply

    Just want to say thanks for the code, its really helped me develop my first application. Thanks to you my first app should be in the market place soon enough.

    I’ve one question, How does the Android OS know that Tutmenu.java is the first file that should be run, i can’t see anything in the code that mentions it. I’m guessing its specified somewhere else the code?

    Thanks

    • blundell
      Posted March 11, 2012 at 7:55 pm | Permalink | Reply

      Glad you made use of it.

      Sorry I can’t see a Tutmenu.java, the first ‘file’ that is run is declared in your AndroidManifest.xml

6 Trackbacks

  1. [...] code is inspired from these two links: This one for the layout idea. http://blog.blundell-apps.com/imageview-with-loading-spinner/ and this one for the cache system. [...]

  2. [...] code is inspired from these two links: This one for the layout idea. http://blog.blundell-apps.com/imageview-with-loading-spinner/ and this one for the cache system. [...]

  3. [...] and not the image itself. Therefore I am going to use code from one of my previous tutorials (UrlImageView) to load the image in an asynchronous manner, this means I’ll just pass the url to the [...]

  4. [...] and not the image itself. Therefore I am going to use code from one of my previous tutorials (UrlImageView) to load the image in an asynchronous manner, this means I’ll just pass the url to the [...]

  5. By Android AsynTask | | FYCoderFYCoder on February 9, 2012 at 7:22 am

Post a Comment

Your email is never shared. Required fields are marked *

*
*