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即可。