Android 自定義 BaseAdapter 最佳實(shí)踐
雖然現(xiàn)在很多新的項(xiàng)目都在使用RecyclerView,但是很多開發(fā)者在一些場(chǎng)景中還是傾向使用ListView或者GridView,然后就是需要寫許多的Adapter。一次項(xiàng)目組在新啟動(dòng)一個(gè)新項(xiàng)目的時(shí)候,有個(gè)同事拿來了一個(gè)網(wǎng)上說的***Adapter,在使用的時(shí)候發(fā)現(xiàn)即使在單個(gè)視圖類型一旦邏輯判斷比較復(fù)雜情況下非常不方便,更不用說在適配器Adapter中使用多視圖類型了,這里僅是個(gè)人觀點(diǎn),也許沒有掌握到精華。
當(dāng)然了隨著RecyclerView的使用,網(wǎng)上也有很多有關(guān)對(duì)RecyclerView多視圖類型Adapter封裝的博客,MultiType 3.0是一個(gè)大神寫的比較全面的Adapter,***適配器Adapter自己使用不是很方便,于是就參看RecyclerView中Adapter的實(shí)現(xiàn)方式進(jìn)行對(duì)BaseAdapter進(jìn)行了簡單的封裝,封裝的目一是為了少寫代碼,另外一個(gè)就是讓邏輯看上去更清晰一些。我們知道在RecyclerView的Adapter實(shí)現(xiàn)中它將視圖創(chuàng)建與數(shù)據(jù)綁定進(jìn)行了分離,同時(shí)將對(duì)View的查找創(chuàng)建也剝離開來了,本文就主要介紹如何將BaseAdapter的使用封裝為跟RecyclerView的Adapter使用方式一致。由于很多時(shí)候在Adapter中我們都是使用的簡單的視圖類型,即單類型視圖,因此本文將單視圖類型的Adapter單獨(dú)封裝了一下,比使用多視圖類型的Adapter使用了更嚴(yán)格的數(shù)據(jù)類型檢查,同時(shí)在使用上也方便了許多。
RecyclerView中Adapter的使用
在使用RecyclerView的Adapter的時(shí)候我們首先需要繼承RecyclerView的一個(gè)靜態(tài)內(nèi)部類Adapter,然后重寫三個(gè)方法,實(shí)際上下面三個(gè)方法是必須要重寫的,因?yàn)槎际浅橄蠓椒ā?/p>
- getItemCount()
- onBindViewHolder(VH holder, int position)
- onCreateViewHolder(ViewGroup parent, int viewType)
一般情況下重寫上面三個(gè)方法就可以,但是如果存在多視圖類型,在第三個(gè)方法
onCreateViewHolder()方法中我們也可以看到有一個(gè)參數(shù)是viewType,該參數(shù)作用就是針對(duì)不同的viewType需要?jiǎng)?chuàng)建不同的ViewHolder,因此還需要重寫一個(gè)方法getItemViewType(int position),針對(duì)多視圖類型同BaseAdapter實(shí)現(xiàn)方式倒是很像,在BaseAdapter中這是需要除此之外還要重寫一個(gè)方法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
在自定義基類之前,先簡單分析一下,我們需要自定義一個(gè)支持單種視圖的Adapter,還要自定義一個(gè)支持多種視圖類型的Adapter,兩個(gè)類都要繼承BaseAdapter,先將兩個(gè)類都公用的部分抽取出來定義為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實(shí)現(xiàn)中是沒有g(shù)etView()方法的,下面我們就分析一下getView()方法如何拆分,一般情況下我們?cè)趯?shí)現(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;
- }
- //一個(gè)空的ViewHolder
- public static class ViewHolder{
- TextView textView;
- ImageView imageView;
- }
Java編程比較流行的一種編程方式不是說面向接口編程嗎,在Android開發(fā)中也有一個(gè)開發(fā)方式叫做面向Holder的編程,上面代碼是傳統(tǒng)的實(shí)現(xiàn)ViewHolder的方式,說句實(shí)現(xiàn)話就沒做什么事,就是作為一個(gè)載體承載著我們需要的控件。我們讓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。由于不同的視圖需要?jiǎng)?chuàng)建不同的ViewHolder,因此我們可以將創(chuàng)建ViewHolder的方法設(shè)置為抽象的方法暴露出去,另外賦值的時(shí)候我們也需要根據(jù)具體的業(yè)務(wù)進(jìn)行賦值,同樣設(shè)置一個(gè)抽象方法。
- 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的時(shí)候只需要在創(chuàng)建ViewHolder的時(shí)候多傳入一個(gè)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);
- }
- }
- }
這里我們使用了兩個(gè)泛型,一個(gè)是ViewHolder中支持的數(shù)據(jù)類型String,另外一個(gè)就是我們需要?jiǎng)?chuàng)建的ViewHolder,這樣在onCreateViewHolder方法的返回值就會(huì)自動(dòng)返回我們自定義的ViewHolder,有關(guān)泛型更多的知識(shí)可以參看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的使用幾乎是一模一樣,唯一不一樣的地方就是多寫了一個(gè)getViewTypeCount()方法,在ListView或者GridView使用BaseAdapter實(shí)現(xiàn)多種類型視圖的時(shí)候該方法必須要重寫。