Android自定義BaseAdapter最佳實踐
雖然現(xiàn)在很多新的項目都在使用RecyclerView,但是很多開發(fā)者在一些場景中還是傾向使用ListView或者GridView,然后就是需要寫許多的Adapter。一次項目組在新啟動一個新項目的時候,有個同事拿來了一個網(wǎng)上說的***Adapter,在使用的時候發(fā)現(xiàn)即使在單個視圖類型一旦邏輯判斷比較復(fù)雜情況下非常不方便,更不用說在適配器Adapter中使用多視圖類型了,這里僅是個人觀點,也許沒有掌握到精華,這是有關(guān)***適配器Adapter的一片博文 Android 快速開發(fā)系列 打造***的ListView GridView 適配器 。
當(dāng)然了隨著RecyclerView的使用,網(wǎng)上也有很多有關(guān)對RecyclerView多視圖類型Adapter封裝的博客,MultiType 3.0是一個大神寫的比較全面的Adapter,這篇博客Android 復(fù)雜的多類型列表視圖新寫法:MultiType 3.0有詳細(xì)的用法。***適配器Adapter自己使用不是很方便,于是就參看RecyclerView中Adapter的實現(xiàn)方式進(jìn)行對BaseAdapter進(jìn)行了簡單的封裝,封裝的目一是為了少寫代碼,另外一個就是讓邏輯看上去更清晰一些。我們知道在RecyclerView的Adapter實現(xiàn)中它將視圖創(chuàng)建與數(shù)據(jù)綁定進(jìn)行了分離,同時將對View的查找創(chuàng)建也剝離開來了,本文就主要介紹如何將BaseAdapter的使用封裝為跟RecyclerView的Adapter使用方式一致。由于很多時候在Adapter中我們都是使用的簡單的視圖類型,即單類型視圖,因此本文將單視圖類型的Adapter單獨封裝了一下,比使用多視圖類型的Adapter使用了更嚴(yán)格的數(shù)據(jù)類型檢查,同時在使用上也方便了許多。
RecyclerView中Adapter的使用
在使用RecyclerView的Adapter的時候我們首先需要繼承RecyclerView的一個靜態(tài)內(nèi)部類Adapter,然后重寫三個方法,實際上下面三個方法是必須要重寫的,因為都是抽象方法。
- getItemCount()
 - onBindViewHolder(VH holder, int position)
 - onCreateViewHolder(ViewGroup parent, int viewType)
 
一般情況下重寫上面三個方法就可以,但是如果存在多視圖類型,在第三個方法
onCreateViewHolder()方法中我們也可以看到有一個參數(shù)是viewType,該參數(shù)作用就是針對不同的viewType需要創(chuàng)建不同的ViewHolder,因此還需要重寫一個方法getItemViewType(int position),針對多視圖類型同BaseAdapter實現(xiàn)方式倒是很像,在BaseAdapter中這是需要除此之外還要重寫一個方法getViewTypeCount(),但是在RecyclerView的Adapter中不需要該方法。
簡單類型Adapter
- private class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
 - @Override
 - public int getItemCount() {
 - return COUNT;
 - }
 - @Override
 - public void onBindViewHolder(MyViewHolder holder, int position) {
 - holder.textView.setText("TEXT_" + position);
 - }
 - @Override
 - public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 - View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_text, parent, false);
 - MyViewHolder holder = new MyViewHolder(view);
 - return holder;
 - }
 - }
 - private static class MyViewHolder extends RecyclerView.ViewHolder {
 - private TextView textView;
 - public MyViewHolder(View itemView) {
 - super(itemView);
 - textView = (TextView) itemView.findViewById(R.id.textView);
 - }
 - }
 
復(fù)雜類型Adapter
- private class MyAdapter extends RecyclerView.Adapter<ViewHolder> {
 - @Override
 - public int getItemCount() {
 - return COUNT;
 - }
 - @Override
 - public int getItemViewType(int position) {
 - return position % 2 == 0 ? TYPE_IMAGE : TYPE_TEXT;
 - }
 - @Override
 - public void onBindViewHolder(ViewHolder holder, int position) {
 - int type = getItemViewType(position);
 - switch (type) {
 - case TYPE_TEXT:
 - ((MyTextHolder) holder).textView.setText("TEXT_" + position);
 - break;
 - case TYPE_IMAGE:
 - ((MyImageHolder) holder).imageView.setImageResource(R.drawable.image);
 - break;
 - }
 - }
 - @Override
 - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 - View view;
 - ViewHolder holder = null;
 - switch (viewType) {
 - case TYPE_TEXT:
 - view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_text, parent, false);
 - holder = new MyTextHolder(view);
 - break;
 - case TYPE_IMAGE:
 - view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_image, parent, false);
 - holder = new MyImageHolder(view);
 - break;
 - }
 - return holder;
 - }
 - }
 - private class MyTextHolder extends RecyclerView.ViewHolder {
 - private TextView textView;
 - public MyTextHolder(View itemView) {
 - super(itemView);
 - textView = (TextView) itemView.findViewById(R.id.textView);
 - }
 - }
 - private class MyImageHolder extends ViewHolder {
 - private ImageView imageView;
 - public MyImageHolder(View itemView) {
 - super(itemView);
 - imageView = (ImageView) itemView.findViewById(R.id.imageView);
 - }
 - }
 
自定義BaseAdapter
在自定義基類之前,先簡單分析一下,我們需要自定義一個支持單種視圖的Adapter,還要自定義一個支持多種視圖類型的Adapter,兩個類都要繼承BaseAdapter,先將兩個類都公用的部分抽取出來定義為MyAdapter。
- public abstract class MyAdapter<T> extends BaseAdapter {
 - protected List<T> dataList = new ArrayList<>();
 - protected Context context;
 - protected LayoutInflater inflater;
 - public MyAdapter(Context context) {
 - this.context = context;
 - inflater = LayoutInflater.from(context);
 - }
 - public void setDataList(List<T> dataList) {
 - this.dataList = dataList;
 - notifyDataSetChanged();
 - }
 - @Override
 - public int getCount() {
 - if (null == dataList) {
 - return 0;
 - }
 - return dataList.size();
 - }
 - @Override
 - public T getItem(int position) {
 - return dataList.get(position);
 - }
 - @Override
 - public long getItemId(int position) {
 - return position;
 - }
 - }
 
在RecyclerView的Adapter實現(xiàn)中是沒有g(shù)etView()方法的,下面我們就分析一下getView()方法如何拆分,一般情況下我們在實現(xiàn)getView()方法都是如下流程。
- public View getView(int position, View convertView, ViewGroup parent) {
 - ViewHolder holder = null;
 - if (null == convertView) {
 - //填充布局
 - convertView=inflater.inflate(R.layout.item_layout, parent,false);
 - holder = new ViewHolder();
 - //通過ID查詢控件
 - holder.textView=(TextView)convertView.findViewById(R.id.textView);
 - holder.imageView=(ImageView)convertView.findViewById(R.id.imageView);
 - convertView .setTag(holder);
 - } else {
 - holder = (ViewHolder) convertView.getTag();
 - }
 - //賦值邏輯
 - return convertView;
 - }
 - //一個空的ViewHolder
 - public static class ViewHolder{
 - TextView textView;
 - ImageView imageView;
 - }
 
Java編程比較流行的一種編程方式不是說面向接口編程嗎,在Android開發(fā)中也有一個開發(fā)方式叫做面向Holder的編程,上面代碼是傳統(tǒng)的實現(xiàn)ViewHolder的方式,說句實現(xiàn)話就沒做什么事,就是作為一個載體承載著我們需要的控件。我們讓ViewHolder多做一些事情,讓它在convertView==null情況下需要做的多數(shù)邏輯都放到ViewHolder中去。
- public class ViewHolder {
 - private final View itemView;
 - public ViewHolder(View itemView) {
 - if (null == itemView) {
 - throw new IllegalArgumentException("itemView must not be null");
 - } else {
 - this.itemView = itemView;
 - itemView.setTag(this);
 - }
 - }
 - public View getItemView() {
 - return itemView;
 - }
 - }
 
在ViewHolder中的itemView就是getView()方法中的convertView,這里剛好是條目的根View,類似RecyclerView中ViewHolder構(gòu)造方法中itemView。由于不同的視圖需要創(chuàng)建不同的ViewHolder,因此我們可以將創(chuàng)建ViewHolder的方法設(shè)置為抽象的方法暴露出去,另外賦值的時候我們也需要根據(jù)具體的業(yè)務(wù)進(jìn)行賦值,同樣設(shè)置一個抽象方法。
- public abstract class SimpleAdapter<T,VH extends ViewHolder> extends MyAdapter<T> {
 - public SimpleAdapter(Context context) {
 - super(context);
 - }
 - public View getView(int position, View convertView, ViewGroup parent) {
 - VH holder = null;
 - if (null == convertView) {
 - holder = onCreateViewHolder(parent);
 - convertView = holder.getItemView();
 - } else {
 - holder = (VH) convertView.getTag();
 - }
 - onBindViewHolder(holder, position);
 - return convertView;
 - }
 - public abstract void onBindViewHolder(VH holder, int position);
 - public abstract VH onCreateViewHolder(ViewGroup parent);
 - }
 
在設(shè)置多視圖類型的Adapter的時候只需要在創(chuàng)建ViewHolder的時候多傳入一個viewType的參數(shù)即可。
- public abstract class MultiAdapter<T> extends MyAdapter<T> {
 - public MultiAdapter(Context context) {
 - super(context);
 - }
 - @Override
 - public View getView(int position, View convertView, ViewGroup parent) {
 - ViewHolder holder = null;
 - if (null == convertView) {
 - holder = onCreateViewHolder(parent, getItemViewType(position));
 - convertView = holder.getItemView();
 - } else {
 - holder = (ViewHolder) convertView.getTag();
 - }
 - onBindViewHolder(holder, position);
 - return convertView;
 - }
 - public abstract void onBindViewHolder(ViewHolder holder, int position);
 - public abstract ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
 - }
 
自定義BaseAdapter的使用
單視圖類型SimpleAdapter使用
- public class TextAdapter extends SimpleAdapter<String, TextAdapter.TextHolder> {
 - public TextAdapter(Context context) {
 - super(context);
 - }
 - @Override
 - public void onBindViewHolder(TextHolder holder, int position) {
 - holder.textView.setText(getItem(position));
 - }
 - @Override
 - public TextHolder onCreateViewHolder(ViewGroup parent) {
 - View convertView=inflater.inflate(R.layout.item_text, parent, false);
 - return new TextHolder(convertView);
 - }
 - static class TextHolder extends ViewHolder{
 - public TextView textView;
 - public TextHolder(View itemView) {
 - super(itemView);
 - textView=(TextView) itemView.findViewById(R.id.textView);
 - }
 - }
 - }
 
這里我們使用了兩個泛型,一個是ViewHolder中支持的數(shù)據(jù)類型String,另外一個就是我們需要創(chuàng)建的ViewHolder,這樣在onCreateViewHolder方法的返回值就會自動返回我們自定義的ViewHolder,有關(guān)泛型更多的知識可以參看Java泛型使用解析,單視圖類型Adapter的使用比RecyclerView的Adapter還要方便許多。
多視圖類型的使用
- public class RichAdapter extends MultiAdapter<String> {
 - private static final int TEXT = 0;
 - private static final int PIC = 1;
 - public RichAdapter(Context context) {
 - super(context);
 - }
 - @Override
 - public int getViewTypeCount() {
 - return 2;
 - }
 - @Override
 - public int getItemViewType(int position) {
 - if (position % 3 == 0) {
 - return PIC;
 - } else {
 - return TEXT;
 - }
 - }
 - @Override
 - public void onBindViewHolder(ViewHolder holder, int position) {
 - switch (getItemViewType(position)) {
 - case TEXT:
 - TextHolder textHolder=(TextHolder) holder;
 - textHolder.textView.setText(getItem(position));
 - break;
 - case PIC:
 - ImageHolder imageHolder=(ImageHolder) holder;
 - imageHolder.imageView.setImageResource(R.drawable.image);
 - break;
 - }
 - }
 - @Override
 - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 - View itemView = null;
 - ViewHolder holder = null;
 - switch (viewType) {
 - case TEXT:
 - itemView = inflater.inflate(R.layout.item_text, parent, false);
 - holder = new TextHolder(itemView);
 - break;
 - case PIC:
 - itemView = inflater.inflate(R.layout.item_image, parent, false);
 - holder = new ImageHolder(itemView);
 - break;
 - }
 - return holder;
 - }
 - private static class TextHolder extends ViewHolder {
 - TextView textView;
 - public TextHolder(View itemView) {
 - super(itemView);
 - textView = (TextView) itemView.findViewById(R.id.textView);
 - }
 - }
 - private static class ImageHolder extends ViewHolder {
 - ImageView imageView;
 - public ImageHolder(View itemView) {
 - super(itemView);
 - imageView = (ImageView) itemView.findViewById(R.id.imageView);
 - }
 - }
 - }
 
這里的使用情況跟RecyclerView的使用幾乎是一模一樣,唯一不一樣的地方就是多寫了一個getViewTypeCount()方法,在ListView或者GridView使用BaseAdapter實現(xiàn)多種類型視圖的時候該方法必須要重寫。

















 
 
 








 
 
 
 