天天看點

打造适配ListView的萬能BaseAdapter

前言:我們項目中用ListView和RecyclerView的情況會非常多,每次寫Adapter會不會覺得很繁瑣,明明很多代碼都差不多,本文将帶領大家一步一步打造一個萬能BaseListAdapter。

一:一般寫法

  • MainActivity
public class MainActivity extends AppCompatActivity {

    private ListView lv_main;
    private List<Bean> mList;
    private PersonAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv_main = findViewById(R.id.lv_main);

        initData();

        initView();

    }

    private void initData() {
        mList = new ArrayList<>();
        mList.add(new Bean("xiaoming 1", "湖南省長沙市天心區", 18));
        mList.add(new Bean("xiaoming 2", "湖南省長沙市天心區", 18));
        mList.add(new Bean("xiaoming 3", "湖南省長沙市天心區", 18));
        mList.add(new Bean("xiaoming 4", "湖南省長沙市天心區", 18));
        mList.add(new Bean("xiaoming 5", "湖南省長沙市天心區", 18));
        mList.add(new Bean("xiaoming 6", "湖南省長沙市天心區", 18));
    }

    private void initView() {
        mAdapter = new PersonAdapter(mList, this);
        lv_main.setAdapter(mAdapter);
    }
}

           
  • PersonAdapter
public class PersonAdapter extends BaseAdapter {

   private List<Bean> mList;
   private Context mContext;
   private LayoutInflater mInflater;

   public PersonAdapter(List<Bean> mList, Context context) {
       this.mList = mList;
       this.mContext = context;
       mInflater = LayoutInflater.from(context);
   }

   @Override
   public int getCount() {
       return (mList == null) ? 0 : mList.size();
   }

   @Override
   public Object getItem(int position) {
       return mList.get(position);
   }

   @Override
   public long getItemId(int position) {
       return position;
   }

   @Override
   public View getView(int position, View convertView, ViewGroup parent) {
       ViewHolder viewHolder = null;
       if (convertView == null) {
           convertView = mInflater.inflate(R.layout.item_list, parent, false);
           viewHolder = new ViewHolder();
           viewHolder.tv_name = convertView.findViewById(R.id.tv_name);
           viewHolder.tv_address = convertView.findViewById(R.id.tv_address);
           viewHolder.tv_age = convertView.findViewById(R.id.tv_age);
           convertView.setTag(viewHolder);
       } else {
           viewHolder = (ViewHolder) convertView.getTag(position);
       }

       Bean bean = mList.get(position);
       viewHolder.tv_name.setText(bean.getName());
       viewHolder.tv_address.setText(bean.getAddress());
       viewHolder.tv_age.setText("年齡:"+bean.getAge());

       return convertView;
   }


   private class ViewHolder {
       private TextView tv_name;
       private TextView tv_address;
       private TextView tv_age;
   }
}
           
  • 實體類Bean(基礎和萬能Adapter都一樣)
public class Bean {
    private String name;
    private String address;
    private int age;
    ...//省略set,get和construct方法
}
           
  • xml布局(省略)
  • 運作效果
    打造适配ListView的萬能BaseAdapter

二、把ViewHolder抽取出來,打造萬能ViewHolder

  • 建立CommonViewHolder
public class CommonViewHolder {

    private SparseArray<View> mViews;
    private View mConvertView;
    private int mPosition;

    public CommonViewHolder(Context mContext, ViewGroup parent, int position, int resId) {
        this.mPosition = position;
        mViews = new SparseArray<>();
        mConvertView = LayoutInflater.from(mContext).inflate(resId, parent, false);
        mConvertView.setTag(this);
    }

    public static CommonViewHolder get(Context mContext, View convertView, ViewGroup parent, int position, int resId) {
        if (convertView == null) {
            return new CommonViewHolder(mContext, parent, position, resId);
        } else {
            CommonViewHolder viewHolder = (CommonViewHolder) convertView.getTag();
            viewHolder.mPosition = position;
            return viewHolder;
        }
    }

    /**
     * 通過VIewid擷取控件
     *
     * @param viewId
     * @param <T>
     * @return
     */
    public <T extends View> T getView(int viewId) {
        T view = (T) mViews.get(viewId);
        if (view == null) {
            view = (T) mConvertView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return view;
    }


    public View getConvertView() {
        return mConvertView;
    }
}
           
  • 修改PersonAdapter中getView()代碼
...//省略
 @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    //這邊一句代碼就做了上邊擷取ViewHolder的工作
        CommonViewHolder viewHolder = CommonViewHolder.get(mContext, convertView, parent, position, R.layout.item_list);

        Bean bean = mList.get(position);
        //使用CommonViewHolder中的getView方法設定資料
        ((TextView) viewHolder.getView(R.id.tv_name)).setText(bean.getName());
        ((TextView) viewHolder.getView(R.id.tv_address)).setText(bean.getAddress());
        ((TextView) viewHolder.getView(R.id.tv_age)).setText("年齡:" + bean.getAge());

        return viewHolder.getConvertView();
    }
    ...//省略
           
  • 次改後運作上述代碼,發現運作成功。我們的代碼簡潔許多,萬能ViewHolder初步打造完成。

三、完善CommonViewHolder,可以看到上部分有很多setText方法,我們将setText抽取出來,相應的還可以抽取setImageResource等等。

  • 修改CommonViewHolder
...//省略
 public  CommonViewHolder setText(int viewId,String text){
        ((TextView)getView(viewId)).setText(text);
        return this;
    }

    public CommonViewHolder setImageResource(int viewId,int resId){
        ((ImageView)getView(viewId)).setImageResource(resId);
        return this;
    }
    ...//省略
           
  • 相應修改PerAdapter代碼
Bean bean = mList.get(position);
viewHolder.setText(R.id.tv_name,bean.getName())
                .setText(R.id.tv_address,bean.getAddress())
                .setText(R.id.tv_age,"年齡:" + bean.getAge());
           
  • 修改後運作成功,我們的CommonViewHolder打造完成,當然,除了setText、setImageResource,你還可以根據項目需要寫一些需要的方法。

四、打造萬能Adapter。(在寫代碼的過程中,我們會發現大部分的adapter,getCount、getItem、getItemId寫的代碼都差不多,初步我們就将它們提取出來)

  • 打造CommonListAdapter,提取getCount、getItem、getItemId
public abstract class CommonListAdapter<T> extends BaseAdapter {
    //泛型List
    private List<T> mDatas;

    public CommonListAdapter(List<T> mDatas) {
        this.mDatas = mDatas;
    }

    @Override
    public int getCount() {
        return (mDatas == null)?0:mDatas.size();
    }

    @Override
    public Object getItem(int position) {
        return mDatas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public abstract View getView(int position, View convertView, ViewGroup parent);
}

           
  • 修改PersonAdapter代碼
public class PersonAdapter extends CommonListAdapter {
    private Context mContext;
    private List<Bean> mList;

    public PersonAdapter(List mDatas,Context context) {
        super(mDatas);
        this.mContext = context;
        this.mList = mDatas;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        CommonViewHolder viewHolder = CommonViewHolder.get(mContext, convertView, parent, position, R.layout.item_list);

        Bean bean = mList.get(position);
        ((TextView) viewHolder.getView(R.id.tv_name)).setText(bean.getName());
        ((TextView) viewHolder.getView(R.id.tv_address)).setText(bean.getAddress());
        ((TextView) viewHolder.getView(R.id.tv_age)).setText("年齡:" + bean.getAge());

        return viewHolder.getConvertView();
    }
}
           
  • 運作代碼,嗯,跟我們之前的效果一模一樣,Adapter的代碼量也相應減少。萬能Adapter初步完成。

五、我們可以看到,CommonListAdapter中我們傳遞了資料mData,但是PersonAdapter的getView中依然要擷取,而且viewHolder 和最後return也是差不多的寫法,我們每次布局不可能一樣,我們是否可以再次優化CommonListAdapter中的getView呢?

  • 再次優化CommonListAdapter
public abstract class CommonListAdapter<T> extends BaseAdapter {
    //泛型List
    private List<T> mDatas;
    private Context mContext;
    private int mLayoutId;

 public void setmDatas(List<T> mDatas) {
        this.mDatas = mDatas;
    }

    public CommonListAdapter(Context context,,int layoutId) {
        this.mContext = context;
        this.mLayoutId = layoutId;
    }

...//省略

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        CommonViewHolder viewHolder = CommonViewHolder.get(mContext, convertView, parent, position,mLayoutId);
        T bean = mDatas.get(position);

        convert(viewHolder,bean);

        return viewHolder.getConvertView();
    }

    public abstract void convert(CommonViewHolder viewHolder,T bean);
}

           
  • 再來寫我們的PersonAdapter
public class PersonAdapter extends CommonListAdapter<Bean> {
    public PersonAdapter(List mDatas,Context context) {
       super(context,layoutId);
        setmDatas(mDatas);
    }
    
    @Override
    public void convert(CommonViewHolder viewHolder,Bean bean) {
        ((TextView) viewHolder.getView(R.id.tv_name)).setText(bean.getName());
        ((TextView) viewHolder.getView(R.id.tv_address)).setText(bean.getAddress());
        ((TextView) viewHolder.getView(R.id.tv_age)).setText("年齡:" + bean.getAge());
    }
}
           
  • 運作,效果杠杠滴(可以看到經過上述幾部已經将PersonAdapter的代碼簡化簡化再簡化了,我們的CommonViewHolder和CommonListAdapter相信也可以滿足大部分的項目需要了)。

六、有些ListView展示的資料很簡單,我們就沒有必要另外寫一個類去繼承CommonListAdapter了,直接使用匿名内部類會簡單些。

  • 修改MainActivity代碼
private void initView() {
//        mAdapter = new PersonAdapter(mList, this);
//這裡使用匿名内部類就不需要再去建立類
        lv_main.setAdapter(new CommonListAdapter<Bean>(mList,MainActivity.this,R.layout.item_list) {
            @Override
            public void convert(CommonViewHolder viewHolder, Bean bean) {
                viewHolder.setText(R.id.tv_name,bean.getName())
                        .setText(R.id.tv_address,bean.getAddress())
                        .setText(R.id.tv_age,"年齡:" + bean.getAge());
            }
        });
    }
           

七、總優化(延伸思考和總結)

  1. 思考:在實際使用過程中,因為還涉及到重新整理,我們需要把資料提取出來單獨寫一個方法設定,還有我們ListVIew的多布局情況,如果用到這些都是需要考慮的。
  2. 總結:平時項目中用到了萬能Adapter,也修改過其中的代碼,這次看了慕課網的課程:https://www.imooc.com/learn/372

    完完整整的理了一遍,還是需要不斷學習。請加油。

  3. 最後貼上demo下載下傳位址,可直接下載下傳運作 https://github.com/xiaomei888/baselistadapter/tree/master