当 listview 有太多项目时 viewpager 滞后
viewpager lagging when listview got too many items
我正在使用 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。
到目前为止一切正常。但是,当我将上述列表视图之一中的项目数量从十个增加到大约五十个项目时,viewpager 不再平滑滑动。
我使用内存和磁盘缓存图片。这是我的图像加载器配置:
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());
我使用以下选项参数:
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();
这是完整的列表视图适配器:
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;
}
}
viewpager 的问题在于它们会在当前视图之前膨胀视图。如果您正在膨胀大量资源(例如您现在正在做的事情),这可能会导致问题。我建议只膨胀列表视图而不是所有信息。您在 activity 中使用 onPageSelected 方法来调用方法以开始加载信息。这样只有活动片段加载。如果您仍然不确定我的意思,请告诉我!
ListView 在滚动出屏幕时不会回收它们的视图。有很多片段,每个渲染图像在一个 ListView 中,可能会非常昂贵。
如果您想避免延迟,请查看 RecyclerView。它们有很好的文档记录,易于实施并且 HEAPS 更高效。
更新:
我注意到您在滚动时加载图像(我认为这不是问题,因为它应该异步加载)。
根据文档,您可以在通过 PauseOnScrollListener
滚动 ListView 时暂停加载。
为避免列表(网格,...)滚动滞后,您可以使用 PauseOnScrollListener:
boolean pauseOnScroll = false; // or true
boolean pauseOnFling = true; // or false
PauseOnScrollListener listener = new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling);
listView.setOnScrollListener(listener);
直接从 Useful Info UIL Documentation 中提取。
希望能解决问题 :)
更新
对于那些想在 RecyclerView 上使用 PauseOnScrollListener 的人,这里是 nostra13 的实现:https://gist.github.com/nostra13/806d01ebd604f3adf241
我正在使用 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。
到目前为止一切正常。但是,当我将上述列表视图之一中的项目数量从十个增加到大约五十个项目时,viewpager 不再平滑滑动。
我使用内存和磁盘缓存图片。这是我的图像加载器配置:
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());
我使用以下选项参数:
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();
这是完整的列表视图适配器:
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;
}
}
viewpager 的问题在于它们会在当前视图之前膨胀视图。如果您正在膨胀大量资源(例如您现在正在做的事情),这可能会导致问题。我建议只膨胀列表视图而不是所有信息。您在 activity 中使用 onPageSelected 方法来调用方法以开始加载信息。这样只有活动片段加载。如果您仍然不确定我的意思,请告诉我!
ListView 在滚动出屏幕时不会回收它们的视图。有很多片段,每个渲染图像在一个 ListView 中,可能会非常昂贵。
如果您想避免延迟,请查看 RecyclerView。它们有很好的文档记录,易于实施并且 HEAPS 更高效。
更新:
我注意到您在滚动时加载图像(我认为这不是问题,因为它应该异步加载)。
根据文档,您可以在通过 PauseOnScrollListener
滚动 ListView 时暂停加载。
为避免列表(网格,...)滚动滞后,您可以使用 PauseOnScrollListener:
boolean pauseOnScroll = false; // or true
boolean pauseOnFling = true; // or false
PauseOnScrollListener listener = new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling);
listView.setOnScrollListener(listener);
直接从 Useful Info UIL Documentation 中提取。
希望能解决问题 :)
更新
对于那些想在 RecyclerView 上使用 PauseOnScrollListener 的人,这里是 nostra13 的实现:https://gist.github.com/nostra13/806d01ebd604f3adf241