导读
之前在学校学习的时候,接触的项目不多没发现,工作之后用到ListView多了很多,不停地写Adapter写的真心烦..后来看到hongyang大神的博客,自己分析又新增了一些内容,让新手学习更容易…
ListView 分析
传统方式: ListView -> Adapter extends BaseAdapter ->ViewHolder
封装方式: ViewHolder 和 CommonAdapter extends BaseAdapter
ListView 封装
通用的ViewHolder类(相当与将传统Adapter getView(..)方法中的内容封装)
- convertView.setTag(holder);
- ViewHolder:Item各种控件的引用
- 使用容器SparseArray 存储通用的View
- 通过分析Adapter 的getView(..) 分析,构造ViewHolder(Context context,ViewGroup parent,int position)
public ViewHolder{
private SparseArray<View> mViews;
protected int mPosition;
private View mConvertView;
private Context mContext;
protected int mLayoutId;
public ViewHolder(Context context, View itemView, ViewGroup parent, int position)
{
mContext = context;
mConvertView = itemView;
mPosition = position;
mViews = new SparseArray<View>();
mConvertView.setTag(this);
}
...
- 写一个对外方法判断convertView是否为空,同时防止ViewHolder被多次实例化 static ViewHolder get(Context context, View convertView,ViewGroup parent, int layoutId, int position)
public static ViewHolder get(Context context, View convertView,
ViewGroup parent, int layoutId, int position)
{
if (convertView == null)
{
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
ViewHolder holder = new ViewHolder(context, itemView, parent, position);
holder.mLayoutId = layoutId;
return holder;
} else
{
ViewHolder holder = (ViewHolder) convertView.getTag();
//复用ConvertView时,position 是会发发生变化的,这里需要更新一下
holder.mPosition = position;
return holder;
}
}
- 分析getView return convertView -> 我们在ViewHolder中
public View getConvertView()
{
return mConvertView;
}
- 模仿传统 ViewHolder.findViewById(..) 通过ViewID获取控件
public <T extends View> T getView(int viewId)
{
View view = mViews.get(viewId);
if (view == null)
{
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
CommonAdapter extends BaseAdapter
- 封装部分传统方法,把getView(…)要实现的方法提供给外部实现
public abstract class CommonAdapter<T> extends BaseAdapter {
protected List<T> mDatas;
protected Context mContext;
protected int mLayoutID;
/**
* @param context 上下文
* @param layoutID_item item 布局ID
* @param datas List<Bean> bean类数组
*/
public CommonAdapter(Context context, final int layoutID_item, List<T> datas) {
this.mContext = context;
this.mDatas = datas;
this.mLayoutID = layoutID_item;
}
@Override
public int getCount() {
//判空,防止空指针异常
if (mDatas != null) {
return mDatas.size();
} else {
return 0;
}
}
@Override
public T getItem(int position) {
//判空,防止空指针异常
if (mDatas != null) {
return mDatas.get(position);
} else {
return null;
}
}
@Override
public long getItemId(int position) {
return position;
}
/**
* ItemInfo infos = mDatas.get(position);
* holder.setText(R.id.tv_title, infos.getTitle());
* holder.setText(R.id.tv_desc, infos.getDesc());
* holder.setText(R.id.tv_data, infos.getData());
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = ViewHolder.get(mContext, convertView, parent, mLayoutID, position);
convert(holder, getItem(position), position);
return holder.getConvertView();
}
/**
* 分析得知需要Item,holder,position
*
* @param holder
* @param item
* @param position
*/
protected abstract void convert(ViewHolder holder, T item, int position);
}
hongyong 大神封装好的
https://github.com/hongyangAndroid/baseAdapter
配合该视频教程使用更佳:
http://www.imooc.com/video/7264
几个关键方法分析:
- isForViewType:
判断外面传进来的item对象和对应的item postion(true 为同一类型,false为不同类型)
默认为false
- 单种ItemViewType
mlistView.setAdapter(new CommonAdapter<String>(this, R.layout.item_list, mDatas) {
@Override
protected void convert(ViewHolder viewHolder, String item, int position) {
viewHolder.setText(R.id.tv, item);
}
});
- 多种ItemViewType
**每种Item对应一个ItemViewDelagate:**
MultiItemTypeAdapter adapter = new MultiItemTypeAdapter(this,mDatas);
adapter.addItemViewDelegate(new MsgSendItemDelagate());
adapter.addItemViewDelegate(new MsgComingItemDelagate());
- ViewHolder 提供了一些辅助方法
1.TextView的各种属性值
2.事件监听
...
要注意的问题:
- Item控件抢占焦点问题
问题:ListView中的Item中有checkBox控件时,item不能被点击,check可以
原因:看ListView源码可知,当Item中有占用焦点的控件,Item失去焦点
解决1:checkBox.setFocusable="false"
解决2:Item的根部局 android:descendantFocusability="blocksDescendants"
- ListView复用导致错乱问题
解决1:在bean类记录下当前这个控件的状态(checkBox为例)
在bean类 ItemInfo 设置一个isChecked 的boolean值
final CheckBox checkBox=holder.getViewByID(cb);
checkBox.setChecked(item.isChecked());
checkBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
item.setChecked(checkBox.isChecked());
}
});
解决2: 写一个数组记录位置(CheckBox为例)
final List<Integer> mPos = new ArrayList<Integer>();
final CheckBox checkBox = holder.getViewByID(cb);
//初始化checkBox时,先默认全部为false,数组拿到关系再设置为
checkBox.setChecked(false);
if(mPos.contains(holder.getPosition())){
checkBox.setChecked(true);
}
checkBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (checkBox.isChecked()) {
mPos.add(holder.getPosition());
} else {
//注意是移除对象
mPos.remove((Integer) holder.getPosition());
}
}
});
- 图片加载在ViewHolder中实现,如
public ViewHolder setImageUrl(int viewId,String url){
ImageView view = getViewByID(viewId);
//Imageloader.getInstance().loadImg(view,url);
return this;
}
源码先不上了,完善ListView 中能添加多种类型Item再补上