一个追求通用调用极简的RecyclerView适配器

RecyclerView使用时,我们通常的业务数据是同类型的,比如都是订单数据,或者都是联系人数据

但有时候我们的数据源可能是不同搞得业务数据,比如文件管理器,每个Itemt条目可能是文件夹,也可能是文件,

并且文件和文件夹的交互也不同,这时相应的视图itemView就是不同的

这两种情况如何兼容到一块?

适配器的职责无非就是创建视图,绑定数据。由特殊到一半,我们先只考虑同类型数据下的适配器:

package com.sun.testrecycleadapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

/**
 * recycleView的通用适配器
 * 主要职责:接收item事件监听,数据crud
 * 对于创建视图绑定数据,给出通用的逻辑
 *
 * @param <T>
 */
public abstract class BaseRecycleAdapter<T> extends RecyclerView.Adapter<RecycleViewHolder>
{
   List<T> mDatas;
   Context mContext;
   int     viewId;
   private int layoutId = -1;

   RecycleViewHolder.OnItemClickListener       mOnItemClickListener;
   RecycleViewHolder.OnItemWidgetClickListener mOnItemWidgetClickListener;

   /**
    * item点击事件监听
    */
   public void setOnItemClickListener(RecycleViewHolder.OnItemClickListener onItemClickListener)
   {
      this.mOnItemClickListener = onItemClickListener;
   }

   /**
    * item中子控件的点击事件监听
    *
    * @param viewId 子控件id
    */
   public void setOnItemWidgetClickListener(int viewId, RecycleViewHolder.OnItemWidgetClickListener itemWidgetClickListener)
   {
      this.mOnItemWidgetClickListener = itemWidgetClickListener;
      this.viewId = viewId;
   }

   public void setLayoutId(int layoutId)
   {
      this.layoutId = layoutId;
   }

   BaseRecycleAdapter(Context context)
   {
      mContext = context;
      mDatas = new ArrayList<>();
   }

   @Override
   public RecycleViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
   {
      if (layoutId == -1)
      {
         throw new IllegalArgumentException("please setLayoutId before you setAdapter");
      }
      RecycleViewHolder holder = RecycleViewHolder.get(mContext, parent, layoutId);
      holder.setOnClickListener(mOnItemClickListener);
      holder.setOnItemWidgetClickListener(viewId, mOnItemWidgetClickListener);
      return holder;
   }

   @Override
   public void onBindViewHolder(RecycleViewHolder holder, int position)
   {
      if (mDatas.size() == 0)
      {
         return;
      }
      bindData(holder, mDatas.get(position));
   }

   public abstract void bindData(RecycleViewHolder holder, T t);

   @Override
   public int getItemCount()
   {
      return mDatas.size();
   }

   /**
    * 添加一个数据对象
    *
    * @param position 添加对象的位置
    * @param t        泛型数据
    */
   public void addData(int position, T t)
   {
      mDatas.add(position, t);
      notifyItemInserted(position);
   }

   /**
    * 添加批量数据对象
    *
    * @param postionStart 添加的起始位置
    * @param list         被添加的数据源
    */
   public BaseRecycleAdapter addDataRange(int postionStart, List<T> list)
   {
      mDatas.addAll(postionStart, list);
      if (mDatas.size() == list.size())
      {
         notifyDataSetChanged();
         return this;
      }
      notifyItemRangeInserted(postionStart, list.size());
      return this;
   }

   /**
    * 清除所有数据对象
    *
    * @return
    */
   public BaseRecycleAdapter clear()
   {
      mDatas.clear();
      notifyDataSetChanged();
      return this;
   }

   /**
    * 移除某个位置的数据对象
    *
    * @param position
    */
   public void removeData(int position)
   {
      mDatas.remove(position);
      notifyItemRemoved(position);
   }

   /**
    * 获取某个位置数据对象
    *
    * @param pos
    * @return
    */
   public T getData(int pos)
   {
      return mDatas.get(pos);
   }

   /**
    * 返回数据集合
    *
    * @return
    */
   public List<T> getDatas()
   {
      return mDatas;
   }
}

上面的适配器是一个功能完整的基类适配器(不完整可后续补充),具有的功能有:

接受item事件监听,包括item自身点击,长按,子控件点击事件

对于创建视图和数据绑定,给出通用的逻辑

对于数据集合的增删操作。

接下来如果我们需要联系人列表:

package com.sun.testrecycleadapter;

import android.content.Context;

/**
 * 联系人适配器,只关注绑定数据
 */
public class ContactAdapter extends BaseRecycleAdapter<Contact>
{

   ContactAdapter(Context context)
   {
      super(context);
   }

   @Override
   public void bindData(RecycleViewHolder holder, Contact contact)
   {
      holder.setText(R.id.tv_contactName, contact.getName());
   }
}

可以看出具体的适配器只需做绑定数据的操作了。

联系人业务bean如下:

package com.sun.testrecycleadapter;

public class Contact
{
   private String name;
   private String phone;

   public Contact(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }

   public String getPhone()
   {
      return phone;
   }

   public void setPhone(String phone)
   {
      this.phone = phone;
   }
}

那么调用呢?

ContactAdapter contactAdapter = new ContactAdapter(this);
contactAdapter.setLayoutId(R.layout.item_contact);
contactAdapter.addDataRange(0, contacts);
contactAdapter.setOnItemClickListener(new RecycleViewHolder.OnItemClickListener()
{
   @Override
   public void onItemClick(View view, int position)
   {
      Toast.makeText(MainActivity.this, "onItemclick:" + position, Toast.LENGTH_SHORT).show();
   }

   @Override
   public void onItemLongClick(View view, int position)
   {
      Toast.makeText(MainActivity.this, "onLongClick:" + position, Toast.LENGTH_SHORT).show();
   }
});
contactAdapter.setOnItemWidgetClickListener(R.id.tv_contactName, new RecycleViewHolder.OnItemWidgetClickListener()
{
   @Override
   public void onItemWidgetClick(View view, int position)
   {
      Toast.makeText(MainActivity.this, "childViewClick:" + position, Toast.LENGTH_SHORT).show();
   }
});
mRecyclerView.setAdapter(contactAdapter);
可以概括为这几步:设置视图布局,设置数据源,设置需要的事件监听。

这样一个对于相同数据类型的适配器就处理完了。

那么对于多类型数据如何处理。还是那句话,适配器的最主要职责就是创建视图,绑定数据。

既然不同的数据类型决定了不同的视图结构,那么我们定义一个多视图基类extends 上面基类,复写onCreatViewHolder()即可

package com.sun.testrecycleadapter;

import android.content.Context;
import android.view.ViewGroup;


/**
 * 主要职责:复写onCreateViewHolder,负责创建不同类型的tiem
 * @param <T>
 */
abstract class MultiItemAdapter<T> extends BaseRecycleAdapter<T>
{
   interface MultiItemTypeSupport<T>
   {
      int getItemViewType(T t);//根据业务bean返回对应的itemView类型

      int getLayoutId(int itemType);//根据itemView类型返回布局文件id
   }

    private MultiItemTypeSupport<T> mMultiItemTypeSupport;

    MultiItemAdapter(Context context, MultiItemTypeSupport<T> multiItemTypeSupport)
   {
      super(context);
      mMultiItemTypeSupport = multiItemTypeSupport;

      if (mMultiItemTypeSupport == null)
         throw new IllegalArgumentException("the mMultiItemTypeSupport can not be null.");
   }

   @Override
   public RecycleViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
   {
      setLayoutId(mMultiItemTypeSupport.getLayoutId(viewType));
      return super.onCreateViewHolder(parent,viewType);
   }

   @Override
   public int getItemViewType(int position)
   {
      return mMultiItemTypeSupport.getItemViewType(mDatas.get(position));
   }
}

如果我们需要文件列表:

package com.sun.testrecycleadapter;

import android.content.Context;

class FileWrapAdapter extends MultiItemAdapter<FileWrap>
{
   FileWrapAdapter(Context context)
   {
      super(context, new MultiItemTypeSupport<FileWrap>()
      {
         @Override
         public int getItemViewType(FileWrap fileWrap)
         {
            return fileWrap.getFileType();
         }

         @Override
         public int getLayoutId(int itemType)
         {
            if (itemType == FileWrap.TYPE_DIR)
            {
               return R.layout.item_dir;
            }
            else
               return R.layout.item_file;
         }
      });
   }

   @Override
   public void bindData(RecycleViewHolder holder, FileWrap fileWrap)
   {
      switch (holder.getLayoutId())
      {
         case R.layout.item_dir:
            //设置文件夹文件名
            holder.setText(R.id.tv_fileName, "我是文件夾");

            break;
         case R.layout.item_file:
            //设置文件名
            String fileName = "a.txt";
            holder.setText(R.id.tv_fileName, fileName);
            holder.setClickable(R.id.upload, true);
            break;
      }
   }
}

可以看出我们除了必须要做的绑定数据外,额外需要做的就是区分视图。

FileWrap业务bean如下:

package com.sun.testrecycleadapter;

public class FileWrap
{

   public static final int TYPE_DIR  = 0;//文件夹
   public static final int TYPE_FILE = 1;//文件

   private int fileType;

   public int getFileType()
   {
      return fileType;
   }

   public void setFileType(int fileType)
   {
      this.fileType = fileType;
   }
}

调用情况:

FileWrapAdapter fileWrapAdapter = new FileWrapAdapter(this);
fileWrapAdapter.addDataRange(0, mFileWrapList);
fileWrapAdapter.setOnItemClickListener(new RecycleViewHolder.OnItemClickListener()
{
   @Override
   public void onItemClick(View view, int position)
   {
      Toast.makeText(MainActivity.this, "onItemclick:" + position, Toast.LENGTH_SHORT).show();
   }

   @Override
   public void onItemLongClick(View view, int position)
   {
      Toast.makeText(MainActivity.this, "onLongClick:" + position, Toast.LENGTH_SHORT).show();
   }
});
fileWrapAdapter.setOnItemWidgetClickListener(R.id.upload, new RecycleViewHolder.OnItemWidgetClickListener()
{
   @Override
   public void onItemWidgetClick(View view, int position)
   {
      Toast.makeText(MainActivity.this, "childViewClick:" + position, Toast.LENGTH_SHORT).show();
   }
});
mRecyclerView.setAdapter(fileWrapAdapter);
这时的调用就只需设置数据集合,需要的监听,布局id的设置交由MultiItemAdapter处理了。

综上,BaseRecycleAdapter和MultiItemAdapter两个文件合起来使用就是兼容这种多数据类型的处理方案。

当数据单一时,直接继承BaseRecycleAdapter即可

当数据多类型是,继承MultiItemAdapter即可。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值