The Missing Widget in the Android SDK: SmartImageView

The Missing Widget in the Android SDK: SmartImageView

Displaying an image from the web in an Android app is unnecessarily hard to do the right way. You need to download it byte by byte from the Internet, store it in a Bitmap object, and then set this Bitmap as the content of the ImageView.  And that’s just the minimal solution; you also want to cache the image so you don’t need to download it each time the ImageView is created, and you ideally want to perform the download off the main UI thread so that your app remains responsive for the user.  The last thing you want to do is spend a long time downloading an image over a poor network connection and leave your app in a locked state until it completes.

While this isn’t insurmountable, it seems like a common problem that should be addressed in the ImageView API.  The first few steps mentioned above–downloading, storing as a Bitmap, and updating the ImageView–can be done using the setImageURI(Uri uri) method. But the documentation for that method states:

“This does Bitmap reading and decoding on the UI thread, which can cause a latency hiccup. If that’s a concern, consider using setImageDrawable(android.graphics.drawable.Drawable) or setImageBitmap(android.graphics.Bitmap) and BitmapFactory instead.”

I’m perplexed that this alternative solution isn’t available in the SDK, but thankfully a few independent developers have made alternatives available as open-source software that we can use in our own applications.  The best I’ve found is a widget called SmartImageView that’s available from James Smith (@loopj).  Let’s take a look at an example for populating an ImageView using somebody’s Twitter profile image.

Using SmartImageView Instead of ImageView

The source code for SmartImageView is available from GitHub, but to use it we just need to download the latest JAR file from the SmartImageView page. Once downloaded, we need to add the JAR file in the “lib” (or “libs”) directory of our Android project.

 Adding the SmartImageView JAR file to the lib directory
Before we forget, let’s make sure that we’ve added the permission in our AndroidManifest.xml file to access the Internet:
<uses-permission android:name="android.permission.INTERNET" />
Now, let’s start with adding a SmartImageView in an XML layout, just like we would do with a normal ImageView. There are two important things to notice here. First is to remember that, when using custom UI components, we need to include the full package name, like com.loopj.android.image.SmartImageView. Second, because SmartImageView extends ImageView, we can use all the same attributes and methods of ImageView in addition to the added functionality.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >

    <com.loopj.android.image.SmartImageView
        android:id="@+id/profileImage"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:scaleType="fitCenter" />

</LinearLayout>

Now, all we need to do is get a reference to the ImageView from the layout like normal and call the setImageUrl() method with the URL as a String parameter:

public class MainActivity extends Activity { 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        SmartImageView profileImage = (SmartImageView) findViewById(R.id.profileImage); 
        profileImage.setImageUrl("https://api.twitter.com/1/users/profile_image?screen_name=bendog24&size=normal");
    }
}

That’s it! The SmartImageView will download the image asynchronously and load it when it’s complete.  It will also cache the image for reuse so we don’t need to re-download it each time we run our app!

Screenshot of SmartImageView in action

If we want to clear out the image, we just call the setImageUrl() method with a blank URL. We can optionally provide a second parameter: the Resource ID of a default image to use instead:

profileImage.setImageUrl("", R.drawable.ic_launcher);

We can also use the SmartImageView to load an image from the user’s Contacts app, which makes use of contact data exposed by an Android Content Provider:

profileImage.setImageUrl(contactAddressBookId);

There are a few other solutions like this, but SmartImageView is so easy to setup and use, and it has worked pretty reliably for me so far. If you know of any similar libraries that are as easy as this, let us know in the comments!

Free Workshops

Watch one of our expert, full-length teaching videos. Choose from HTML, CSS or WordPress.

Start Learning

Ben Jakuben

I'm a dad to a few, husband to one, son to two, brother to one, and friend to many. I spent nine years in software development before finding my dream job with Treehouse. Lately I've been focused on mobile development (primarily Android) as well as how to be a better father/husband/son/friend. Firmly committed to the belief that the world is evolving to a better place. Find me on Twitter @benjakuben.

Comments

Comments are closed.