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;
}
}
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