ListView Adapter 封装 打造Android 万能适配器

导读

之前在学校学习的时候,接触的项目不多没发现,工作之后用到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 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();
        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 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 mDatas;
    protected Context mContext;
    protected int     mLayoutID;

    /**
     * @param context       上下文
     * @param layoutID_item item 布局ID
     * @param datas         List bean类数组
     */
    public CommonAdapter(Context context, final int layoutID_item, List 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(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 的booleanfinal 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 mPos = new ArrayList();

  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再补上

你可能感兴趣的:(工具类)