Search code examples
androidperformancelistviewandroid-viewpageruniversal-image-loader

viewpager lagging when listview got too many items


I'm using a viewpager (SmartTabLayout) with four tabs (fragments). In all four fragments i am using custom listviews. These listviews load images from sd card per item. I use Nostra's universal image loader for that.

Everything works fine that far. But when I increase the number of items in one of the aforementioned listviews from ten to around fifty items, the viewpager doesn't swipe smoothly anymore.

I use memory and disc caching for pictures. Here are my configs of image loader:

File cacheDir = StorageUtils.getOwnCacheDirectory(context, 
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + Constants.DIRECTORY_TEAM_CHANNEL);//for caching

    ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(context);
    config.diskCacheFileNameGenerator(new HashCodeFileNameGenerator());
    config.diskCacheSize(50 * 1024 * 1024); // 50 MiB
    config.diskCache(new UnlimitedDiskCache(cacheDir)); // You can pass your own disc cache implementation
    config.memoryCacheSize(41943040);
    config.threadPoolSize(10);
    ImageLoader.getInstance().init(config.build());

And I use following option parameters:

mImgDisplayOptions = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.drawable.default_user)
            .showImageForEmptyUri(R.drawable.default_user)
            .showImageOnFail(R.drawable.default_user)
            .cacheInMemory(true)
            .cacheOnDisc(true)
            .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
            .build();

Here's the complete listview adapter:

public class CustomContactListAdapter extends BaseAdapter
{
private static final int TYP_REGISTERED = 0;
private static final int TYP_UNREGISTERED = 1;
private static final int VIEW_TYPE_COUNT = 2;

private Activity activity;
private List<User> userItems;
private DisplayImageOptions mOptions;

private DBHandler dbHandler;
private DisplayImageOptions mImgDisplayOptions;

static ImageLoader imageLoader = ImageLoader.getInstance();

public CustomContactListAdapter(Activity activity, List<User> userItems)
{
    this.activity = activity;
    this.userItems = userItems;
    dbHandler = DBHandler.getInstance(activity.getApplicationContext());


    mImgDisplayOptions = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.drawable.default_user)
            .showImageForEmptyUri(R.drawable.default_user)
            .showImageOnFail(R.drawable.default_user)
            .cacheInMemory(true)
            .cacheOnDisc(true)
            .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
            .build();
}

@Override
public int getCount()
{
    return userItems.size();
}

@Override
public Object getItem(int position)
{
    return userItems.get(position);
}

@Override
public long getItemId(int position)
{
    return position;
}

@Override
public int getItemViewType(int position) 
{
    return (userItems.get(position).getToken() != null) ? TYP_REGISTERED : TYP_UNREGISTERED;
}

@Override
public int getViewTypeCount()
{
    return VIEW_TYPE_COUNT;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    ViewHolder viewholder=null;
    int type = getItemViewType(position);    

    if (convertView == null)
    {    
        viewholder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);


        switch(type)
        {
            case TYP_REGISTERED:

                convertView = inflater.inflate(R.layout.contact_list_row, parent, false);
                viewholder.thumbNail = (ImageView ) convertView.findViewById(R.id.thumbnail_reg);
                viewholder.name = (TextView) convertView.findViewById(R.id.name_reg);
                viewholder.lastComment = (TextView) convertView.findViewById(R.id.lastComment_reg);
                viewholder.regState = (TextView) convertView.findViewById(R.id.regState_reg);

                viewholder.thumbNail.setMaxHeight(60);
                viewholder.thumbNail.setMaxWidth(60);
                break;

            case TYP_UNREGISTERED:

                convertView = inflater.inflate(R.layout.contact_list_not_registered_row, parent, false);
                viewholder.thumbNail = (ImageView) convertView.findViewById(R.id.thumbnail_not_reg);
                viewholder.name = (TextView) convertView.findViewById(R.id.name_not_reg);
                viewholder.phonenumber = (TextView) convertView.findViewById(R.id.phonenumber_not_reg);
                viewholder.regState = (TextView) convertView.findViewById(R.id.regState_not_reg);

                viewholder.thumbNail.setMaxHeight(60);
                viewholder.thumbNail.setMaxWidth(60);
                break;
        }

        convertView.setTag(viewholder);            
    }
    else
    {
        viewholder = (ViewHolder) convertView.getTag();
    }

    // getting user data for the row
    User user = userItems.get(position);
    String userName = "";

    switch(type)
    {
        case TYP_REGISTERED:
        {
            //thumbnail
            if (user.getThumbnail() != null)
            {
                if(user.getThumbnail() != null)
                {
                    imageLoader.displayImage(Constants.ABS_PATH_USER_THUMBNAIL + user.getThumbnail() + ".jpg", viewholder.thumbNail, mImgDisplayOptions);
                }
                else
                {
                    viewholder.thumbNail.setImageResource(R.drawable.default_user);
                }
            }
            else
            {
                viewholder.thumbNail.setImageResource(R.drawable.default_user);
            }

            //firstname
            if (user.getFirstname() != null) {
                userName += user.getFirstname() + " ";
            }

            //lastname
            if (user.getLastname() != null) {
                userName += user.getLastname();
            }
            viewholder.name.setText(userName);


            Chat chat = dbHandler.getChatByUser(user);
            if (chat != null) {
                Comment lastComment = dbHandler.getLastCommentFromUser(chat, user);

                if (lastComment != null) {
                    viewholder.lastComment.setText((lastComment.getContent().length() > 25)
                            ? lastComment.getContent().substring(0, 25) + "..." : lastComment.getContent());
                } else {
                    viewholder.lastComment.setText("");
                }

            } else {
                viewholder.lastComment.setText("");
            }


            // registration state (registered)
            viewholder.regState.setText(String.valueOf("mobil"));

            break;
        }
        case TYP_UNREGISTERED:
        {
            if (user.getThumbnail() != null)
            {

                if(user.getThumbnail() != null)
                {
                    imageLoader.displayImage(Constants.ABS_PATH_USER_THUMBNAIL + user.getThumbnail() + ".jpg", viewholder.thumbNail, mImgDisplayOptions);
                }
                else
                {
                    viewholder.thumbNail.setImageResource(R.drawable.default_user);
                }
            }
            else
            {
                viewholder.thumbNail.setImageResource(R.drawable.default_user);
            }

            if (user.getFirstname() != null) {
                userName += user.getFirstname() + " ";
            }

            if (user.getLastname() != null) {
                userName += user.getLastname();
            }

            viewholder.name.setText(userName);


            // phonenumber
            viewholder.phonenumber.setText(user.getPhonenumber());

            // registration state (unregistered, send invitation)
            viewholder.regState.setText(String.valueOf("Einladen"));

            break;
        }
    }

    return convertView;
}

private static class ViewHolder
{
    ImageView thumbNail;
    TextView name;
    TextView phonenumber;
    TextView lastComment;
    TextView regState; 
}
}

Solution

  • ListViews don't recycle their views when they scroll off the screen. Having lots of fragments, each rendering images in a ListView, can be quite costly.

    Have a look into the RecyclerView if you want to avoid lag. They're quite well documented, easy to implement and HEAPS more efficient.

    Update:
    I noticed that you that you're loading images while you're scroll (which I didn't think would be a problem since it should be loading asynchronously).

    According to the documentation, you can pause loading while you scroll ListViews via the PauseOnScrollListener.
    To avoid list (grid, ...) scrolling lags you can use PauseOnScrollListener:

    boolean pauseOnScroll = false; // or true
    boolean pauseOnFling = true; // or false
    PauseOnScrollListener listener = new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling);
    listView.setOnScrollListener(listener);
    

    Took that straight out of the Useful Info UIL Documentation.
    Hopefully that solves the problem :)

    Update
    For those who want to use PauseOnScrollListener on a RecyclerView, here's nostra13's implementation: https://gist.github.com/nostra13/806d01ebd604f3adf241