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<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 的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<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再補上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章