Android ListView使用
一、ListView的使用
1、适配器代码
import java.util.List; import cn.com.ista.pdachina.R; import cn.com.ista.pdachina.bean.Task; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; /* * 任务列表数据适配器 * @author guopeng * @created 2015年11月19日 */ public class TaskListAdapter extends BaseAdapter { private Context context;//运行上下文 private LayoutInflater listContainer;//视图容器,用来导入布局 private List<自定义类> listitems;//数据集合 static class ViewHolder { private LinearLayout layout; private ImageView imageView; private TextView taskNumber; private TextView taskLocation; private TextView taskPostCode; private TextView taskCity; } /* * 实例化Adapter */ public TaskListAdapter(Context context, List<Task> listItems) { this.context = context; this.listContainer = LayoutInflater.from(context); //创建视图容器并设置上下文 this.listitems = listItems; } @Override public int getCount() { return listitems.size(); } @Override public Object getItem(int position) { return listitems.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Task task = listitems.get(position); ViewHolder holder = null; if(convertView == null) { holder = new ViewHolder(); //获取tasklist布局文件的视图 convertView = listContainer.inflate(R.layout.tasklist_listitem, null); //获取控件对象 holder.layout = (LinearLayout) convertView.findViewById(R.id.tasklist_listitem_layout); holder.taskNumber = (TextView) convertView.findViewById(R.id.tasklist_listitem_tasknum); holder.imageView= (ImageView) convertView.findViewById(R.id.tasklist_listitem_image); holder.taskLocation = (TextView) convertView.findViewById(R.id.tasklist_listitem_tasklocation); holder.taskPostCode = (TextView) convertView.findViewById(R.id.tasklist_listitem_taskpostcode); holder.taskCity = (TextView) convertView.findViewById(R.id.tasklist_listitem_taskcity); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } //设置图片和文字 switch (task.getExecutionState()) { case 0: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_undisposed_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_undisposed_mark); break; case 1: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_beingprocessed_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_beingprocessed_mark); break; case 2: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_completed_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_completed_mark); break; case 3: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_canceled_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_canceled_mark); break; } holder.taskNumber.setText(String.valueOf(task.getId())); holder.taskLocation.setText(task.getPropertyName()+task.getBuildingName()); holder.taskPostCode.setText(task.getZipCode()); holder.taskCity.setText(task.getCity()); return convertView; } }
ViewHolder重用时候一定要注意先获取ViewHolder对象,然后赋值,不然就可能出现滑动时数据显示错乱问题。
2、调用
private void initData() { List<自定义类> data;final TaskListAdapter adapter = new TaskListAdapter(appContext, data); listView.setAdapter(adapter); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { } }); }
二、Listview性能优化(ViewHolder重用机制)
ListView优化主要有下面几个方面:
1、convertView重用
2、ViewHolder的子View复用
3、缓存数据复用
1、convertView重用
首先讲下ListView的原理:ListView中的每一个Item显示都需要Adapter调用一次getView()的方法,这个方法会传入一个convertView的参数,这个方法返回的View就是这个Item显示的View。如果当Item的数量足够大,再为每一个Item都创建一个View对象,必将占用很多内存空间,即创建View对象(mInflater.inflate(R.layout.lv_item, null);从xml中生成View,这是属于IO操作)是耗时操作,所以必将影响性能。Android提供了一个叫做Recycler(反复循环)的构件,就是当ListView的Item从滚出屏幕视角之外,对应Item的View会被缓存到Recycler中,相应的会从生成一个Item,而此时调用的getView中的convertView参数就是滚出屏幕的缓存Item的View,所以说如果能重用这个convertView,就会大大改善性能。
那么,我们怎么重用它呢?贴代码:
@Override public View getView(int position, View convertView, ViewGroup parent) { Task task = listitems.get(position); ViewHolder holder = null; if(convertView == null) { holder = new ViewHolder(); //获取tasklist布局文件的视图 convertView = listContainer.inflate(R.layout.tasklist_listitem, null); //获取控件对象 holder.layout = (LinearLayout) convertView.findViewById(R.id.tasklist_listitem_layout); holder.taskNumber = (TextView) convertView.findViewById(R.id.tasklist_listitem_tasknum); holder.imageView= (ImageView) convertView.findViewById(R.id.tasklist_listitem_image); holder.taskLocation = (TextView) convertView.findViewById(R.id.tasklist_listitem_tasklocation); holder.taskPostCode = (TextView) convertView.findViewById(R.id.tasklist_listitem_taskpostcode); holder.taskCity = (TextView) convertView.findViewById(R.id.tasklist_listitem_taskcity); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); }
当这个convertView不存在时,即第一次使用它,我们就创建一个item布局的View对象并赋给convertView,以后使用convertView时,只需从convertView中getTag取出来就可以,不需要再次创建item的布局对象了,这样便提高了性能。
2、使用ViewHolder重用
我们都知道在getView()方法中的操作是这样的:先从xml中创建view对象(inflate操作,我们采用了重用convertView方法优化),然后在这个view去findViewById,找到每一个item的子View的控件对象,如:ImageView、TextView等。这里的findViewById操作是一个树查找过程,也是一个耗时的操作,所以这里也需要优化,就是使用ViewHolder,把每一个item的子View控件对象都放在Holder中,当第一次创建convertView对象时,便把这些item的子View控件对象findViewById实例化出来并保存到ViewHolder对象中。然后用convertView的setTag将viewHolder对象设置到Tag中, 当以后加载ListView的item时便可以直接从Tag中取出复用ViewHolder对象中的,不需要再findViewById找item的子控件对象了。这样便大大提高了性能。
贴个完整的代码:
@Override public View getView(int position, View convertView, ViewGroup parent) { Task task = listitems.get(position); ViewHolder holder = null; if(convertView == null) { holder = new ViewHolder(); //获取tasklist布局文件的视图 convertView = listContainer.inflate(R.layout.tasklist_listitem, null); //获取控件对象 holder.layout = (LinearLayout) convertView.findViewById(R.id.tasklist_listitem_layout); holder.taskNumber = (TextView) convertView.findViewById(R.id.tasklist_listitem_tasknum); holder.imageView= (ImageView) convertView.findViewById(R.id.tasklist_listitem_image); holder.taskLocation = (TextView) convertView.findViewById(R.id.tasklist_listitem_tasklocation); holder.taskPostCode = (TextView) convertView.findViewById(R.id.tasklist_listitem_taskpostcode); holder.taskCity = (TextView) convertView.findViewById(R.id.tasklist_listitem_taskcity); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } //设置图片和文字 switch (task.getExecutionState()) { case 0: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_undisposed_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_undisposed_mark); break; case 1: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_beingprocessed_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_beingprocessed_mark); break; case 2: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_completed_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_completed_mark); break; case 3: holder.layout.setBackgroundResource(R.drawable.tasklist_listitem_canceled_bg); holder.imageView.setBackgroundResource(R.drawable.tasklist_listitem_canceled_mark); break; } holder.taskNumber.setText(String.valueOf(task.getId())); holder.taskLocation.setText(task.getPropertyName()+task.getBuildingName()); holder.taskPostCode.setText(task.getZipCode()); holder.taskCity.setText(task.getCity()); return convertView; }
static class ViewHolder { private LinearLayout layout; private ImageView imageView; private TextView taskNumber; private TextView taskLocation; private TextView taskPostCode; private TextView taskCity; }