Header image

Optimize a ListView with images from Internet | Tutorials

Publicado por Agustin Batuecas

Esta entrada también está disponible en: Spanish

 How to optimize a listview in Android?

When customizing a ListView on Android with more components than just text or images stored on the device, many other factors  should be taken into account such as those which mostly affect the user experience and device memory management .

We will start from a simple TextView whose items will be an ImageView and a TextView. Below I detail the layout for the elements of the list:

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<ImageView
android:id="@+id/image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp" />

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium" />

</LinearLayout>

A first optimization allows us to download images from internet so that the main thread (which takes into account user interaction) is not affected. Thus, the ListView scroll is fluid and does not wait for the images to be downloaded to allow user events. Following the steps that Gilles Debunne comments on the Android Developers Blog , we will have a utility class to download images and assign them to a ImageView by a AsyncTask. We can also add a simple cache for downloaded images recently, so that we don’t need to repeat the download.

This first optimization is a problem from a certain number of items in the list, since it generates a lot of traffic data and in quick scrolls it launches so many AsyncTask, so the performance declines significantly and even causes exceptions because of the number of tasks. Our second optimization is addressed to the adapter from the list, more specifically its getView(int position, View convertView, ViewGroup parent). We will create an efficient adapter that reuses the views of its elements and thus reduce their number without collapsing memory with objects. This is done by reassigning the view that comes as a parameter in the method getView(). We also use a static class that will contain our ImageView and TextView, which will significantly reduce the number of calls to method findViewById(int id), that is time consuming.

This is how our adapter, along with the auxiliary class, should be:

public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder;
if (convertView == null) {

convertView = inflater.inflate(R.layout.list_item, parent, false);
holder = new ViewHolder();
holder.image = (ImageView)convertView.findViewById(R.id.image);
holder.text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(holder);

else holder = (ViewHolder) convertView.getTag();

imagesDownloader.download("La url de la imagen", holder.image);
holder.text.setText("Nuestro texto");

return convertView;

}

static class ViewHolder {

public ImageView image;
public TextView text;

}

 

With this optimization we have saved significant memory problems and avoided AsyncTask being launched for each item in the list to make a scroll very fast, since the elements of the list are recycled as they enter/exit in the portion of the list you see in the display.

Finally, we will use the ScrollListener that can be assigned to any ListView to avoid images to download while we scroll, so only when the list is stopped it will download images of their Internet elements. In this way we gain in efficiency, which has a direct impact on the user interaction with the list. It is interesting to leverage our utility class to download images in the background that allows us to assign a default image that will be displayed for the duration of the loading process of the actual image, and that will be used while the list is not stopped. The Activity that hosts our list must implement therefore OnScrollListener, whose methods are:

public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {

}

public void onScrollStateChanged(AbsListView view, int scrollState) {

switch (scrollState) {

case OnScrollListener.SCROLL_STATE_IDLE:
mBusy = false;
mAdapter.notifyDataSetChanged();
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
mBusy = true;
break;
case OnScrollListener.SCROLL_STATE_FLING:
mBusy = true;
break;

}

}

With the flag mBusy we control the image that is going to be shown in our list through the method getView() in the following way:

if (!mBusy) {

imagesDownloader.download("image url", holder.image);

} else holder.image.setImageResource("default image");

With this series of optimizations we’ll have lists of objects that can extend as much as we want without introducing problems and keeping user interaction and memory usage very tight. This will encourage the impression that cause our Android applications.



You can follow any responses to this entry through the RSS 2.0 Both comments and pings are currently closed.

Follow

Get every new post on this blog delivered to your Inbox.

Join 24 other followers:

Or subscribe to the RSS feed by clicking on the counter:

Startcapps Feedburner