How to load the Listview "smoothly" in android

All we need is an easy explanation of the problem, so here it is.

I load data from Cursor to listview, but my Listview not really display “smooth”. The data change when I drag up and down on the scollbar in my ListView. And some items look like duplicate display in my list.
I hava a “complex ListView” (two textview, one imageview) So I used newView(), bindView() to display data. Can someone help me?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

I will describe you how to get such issue that you have. Possibly this will help you.

So, in list adapter you have such code:

public View getView(int position, View contentView, ViewGroup arg2)
    {
        ViewHolder holder;

        if (contentView == null) {
            holder = new ViewHolder();
            contentView = inflater.inflate(R.layout.my_magic_list,null);
            holder.label = (TextView) contentView.findViewById(R.id.label);
            contentView.setTag(holder);
        } else {
            holder = (ViewHolder) contentView.getTag();
        }

        holder.label.setText(getLabel());

        return contentView;
    }

As you can see, we set list item value only after we have retrieved holder.

But if you move code into above if statement:

holder.label.setText(getLabel());

so it will look after like below:

if (contentView == null) {
   holder = new ViewHolder();
   contentView = inflater.inflate(R.layout.my_magic_list,null);
   holder.label = (TextView) contentView.findViewById(R.id.label);
   holder.label.setText(getLabel());
   contentView.setTag(holder);
}

you will have your current application behavior with list item duplication.

Possibly it will help.

Method 2

ListView is a tricky beast.

Your second question first: you’re seeing duplicates because ListView re-uses Views via convertView, but you’re not making sure to reset all aspects of the converted view. Make sure that the code path for convertView!=null properly sets all of the data for the view, and everything should work properly.

You’ll want your getView() method to look roughly like the following if you’re using custom views:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final MyCustomView v = convertView!=null ? (MyCustomView)convertView : new MyCustomView();
    v.setMyData( listAdapter.get(position) );
    return v;
}

If you’re not using your own custom view, just replace the call to new MyCustomView() with a call to inflater.inflate(R.layout.my_layout,null)

As to your first question, you’ll want to watch Romain’s techtalk on ListView performance here: http://code.google.com/events/io/sessions/TurboChargeUiAndroidFast.html

From his talk and in order of importance from my own experience,

  • Use convertView
  • If you have images, don’t scale your images on the fly. Use Bitmap.createScaledBitmap to create a scaled bitmap and put that into your views
  • Use a ViewHolder so you don’t have to call a bunch of findViewByIds() every time
  • Decrease the complexity of the views in your listview. The fewer subviews, the better. RelativeLayout is much better at this than, say, LinearLayout. And make sure to use if you’re implementing custom views.

Method 3

I’m facing this problem as well, but in my case I used threads to fetch the external images. It is important that the current executing thread do not change the imageView if it is reused!

public View getView(int position, View vi, ViewGroup parent) {
ViewHolder holder;
String imageUrl = ...;

if (vi == null) {
    vi = inflater.inflate(R.layout.tweet, null);
    holder = new ViewHolder();
    holder.image = (ImageView) vi.findViewById(R.id.row_img);
    ...
    vi.setTag(holder);
} else {
    holder = (ViewHolder) vi.getTag();      
}
holder.image.setTag(imageUrl);
...
DRAW_MANAGER.fetchDrawableOnThread(imageUrl, holder.image);
}

And then on the fetching thread I’m doing the important check:

final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
     // VERY IMPORTANT CHECK
    if (urlString.equals(url))
        imageView.setImageDrawable((Drawable) message.obj);
};

Thread thread = new Thread() {  

@Override
public void run() {
    Drawable drawable = fetchDrawable(urlString);
    if (drawable != null) {
        Message message = handler.obtainMessage(1, drawable);
        handler.sendMessage(message);
    }
}};
thread.start();

One could also cancel the current thread if their view is reused (like it is described here), but I decided against this because I want to fill my cache for later reuse.

Method 4

Just one tip: NEVER use transparent background of item layout – it slows performance greatly

Method 5

You can see the reocurring text in multiple rows if you handle it in the wrong way. I’ve blogged a bit about it recently – see here. Other than that you might want to take a look at ListView performance optimization. Generally it’s because of the view reuse and I’ve seen it few times already.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply