滑动列表是最常见的UI界面,也常见卡顿问题。
今天看下两种列表的使用上面有什么区别。
ListView
ListView使用非常简单,但是也容易出现卡顿问题。默认情况下是比较耗时的。下图一帧的耗时是118毫秒。
如果对ConvertView进行复用,可以在一定程度上进行优化,避免多次inflate。下图优化后依然卡顿,时间是80毫秒。
如果增加一个ViewHolder缓存,就可以避免多次findViewById。下图优化后依然卡顿,时间是58毫秒。
最后,可以把一些耗时操作通过AsyncTask实现异步,下图优化后依然卡顿,时间是36毫秒。
public class MyAdapter extends ArrayAdapter<ApplicationInfo> {
private static final String TAG = "MyAdapter";
private PackageManager pms;
private Context context;
public MyAdapter(Context context, int resource) {
super(context, resource);
this.context = context;
pms = context.getPackageManager();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d(TAG, "getView position=" + position);
ApplicationInfo info = getItem(position);
View infoView = convertView;
ViewHolder viewHolder;
if (infoView == null) {
infoView = LayoutInflater.from(context).inflate(R.layout.layout_list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView)infoView.findViewById(R.id.img);
viewHolder.textView = (TextView)infoView.findViewById(R.id.text);
infoView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) infoView.getTag();
}
viewHolder.textView.setText(pms.getApplicationLabel(info));
new LoadTask(viewHolder).execute(info);
return infoView;
}
private static class ViewHolder {
ImageView imageView;
TextView textView;
}
private class LoadTask extends AsyncTask<ApplicationInfo, Void, Drawable> {
private ViewHolder viewHolder;
public LoadTask(ViewHolder viewHolder) {
this.viewHolder = viewHolder;
}
@Override
protected void onPostExecute(Drawable drawable) {
viewHolder.imageView.setImageDrawable(drawable);
}
@Override
protected Drawable doInBackground(ApplicationInfo... infos) {
return pms.getApplicationIcon(infos[0]);
}
}
}
ListView有时会遇到瓶颈,上面的代码多次优化后依然卡顿,可以选择使用RecyclerView。
RecyclerView
RecyclerView可以实现更强大的功能,比如横向滑动,瀑布流布局等。同时默认有ViewHolder缓存机制,滑动更流畅。
public class HorizontalAdapter extends RecyclerView.Adapter<HorizontalAdapter.ViewHolder> {
private static final String TAG = "HorizontalAdapter";
private List<ApplicationInfo> list;
private PackageManager pms;
public HorizontalAdapter(List<ApplicationInfo> list) {
this.list = list;
}
public void setContext(Context context) {
pms = context.getPackageManager();
Log.d(TAG, "getInstalledApplications size=" + list.size());
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder");
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_horizontal_item,
parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder position=" + position);
ApplicationInfo info = list.get(position);
new LoadTask(holder).execute(info);
String label = pms.getApplicationLabel(info).toString();
if (label.length() > 6) {
label = label.substring(0, 6);
}
holder.textView.setText(label);
}
@Override
public int getItemCount() {
return list.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public ViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.img);
textView = itemView.findViewById(R.id.text);
}
}
private class LoadTask extends AsyncTask<ApplicationInfo, Void, Drawable> {
private ViewHolder viewHolder;
public LoadTask(ViewHolder viewHolder) {
this.viewHolder = viewHolder;
}
@Override
protected void onPostExecute(Drawable drawable) {
viewHolder.imageView.setImageDrawable(drawable);
}
@Override
protected Drawable doInBackground(ApplicationInfo... infos) {
return pms.getApplicationIcon(infos[0]);
}
}
}