在項(xiàng)目中大量使用了RecyclerView控件來(lái)展示復(fù)雜的列表,關(guān)于RecyclerView寫(xiě)的最多的就是Adapter了。
對(duì)于Adapter一般都繼承RecyclerView.Adapter復(fù)寫(xiě)幾個(gè)方法,寫(xiě)下來(lái)發(fā)現(xiàn)大部分的代碼基本都是類(lèi)似的。
本篇開(kāi)始一步一步封裝出一個(gè)通用的Adapter。
本篇是對(duì)單類(lèi)型Item的Adapter的封裝
RecyclerView 的Adapter分析
要想分裝出一個(gè)通用的Adapter,首先需要對(duì)Adapter中的代碼進(jìn)行分析,再試著進(jìn)行抽取
public class GankAdapter extends RecyclerView.Adapter<GankAdapter.MyViewHolder> {
private Context mContext;
private List<GankBean> mGankBeanList;
private LayoutInflater mLayoutInflater;
public GankAdapter(Context context, List<GankBean> gankBeanList) {
this.mContext = context;
this.mGankBeanList = gankBeanList;
mLayoutInflater = LayoutInflater.from(mContext);
}
public void addData(int position, List<GankBean> data) {
if (data != null && data.size() > 0) {
mGankBeanList.addAll(position, data);
notifyItemRangeInserted(position, data.size());
}
}
public void setData(List<GankBean> list) {
if (list != null) {
mGankBeanList = list;
notifyDataSetChanged();
}
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View contentView = mLayoutInflater.inflate(R.layout.item_list, parent, false);
return new MyViewHolder(contentView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
GankBean gankBean = mGankBeanList.get(position);
holder.tvTitle.setText(gankBean.getDesc());
holder.tvUrl.setText(gankBean.getUrl());
}
@Override
public int getItemCount() {
return mGankBeanList == null ? 0 : mGankBeanList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tvTitle;
public TextView tvUrl;
public MyViewHolder(View itemView) {
super(itemView);
tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
tvUrl = (TextView) itemView.findViewById(R.id.tv_url);
}
}
}
繼承與RecyclerView.Adapter一般需要重寫(xiě)以下方法:
1、構(gòu)造函數(shù),傳入數(shù)據(jù)和Context對(duì)象
2、getItemCount() 返回Item 的個(gè)數(shù)
3、getItemType()返回Item 的類(lèi)型(本篇只做單類(lèi)型的封裝下篇在講)
4、onCreateViewHolder()創(chuàng)建ViewHolder
5、onBindViewHolder()綁定ViewHolder的數(shù)據(jù)
其中有一個(gè)很關(guān)鍵的角色 就是ViewHolder,ViewHolder 的作用是什么呢?
ViewHolder的主要的作用是存儲(chǔ)對(duì)應(yīng)的convertView中需要操作的子View,避免每次findViewById,從而提升運(yùn)行的效率;
ViewHolder 的封裝
經(jīng)過(guò)以上分析,ViewHolder 的作用是存儲(chǔ)需要操作的子View,對(duì)于不同的ViewHolder我們不知道需要存儲(chǔ)的View 是什么類(lèi)型,有多少數(shù)量
所以只能通過(guò)集合的方式來(lái)存儲(chǔ)子View,存儲(chǔ)的容器采用SparseArray容器,相當(dāng)于一個(gè)Hash<Intager,Object>,可以通過(guò)int型key來(lái)取出value
而關(guān)于子View,我們知道的恰恰是id,為int型,剛好符合了需求。
public class ViewHolder extends RecyclerView.ViewHolder
{
private SparseArray<View> mViews; //存儲(chǔ)子View
private View mConvertView;
private Context mContext;
//構(gòu)造函數(shù) 初始化容器和contvertView
public ViewHolder(Context context, View itemView){
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<>();
}
//根據(jù)ItemView 創(chuàng)建ViewHolder
public static ViewHolder createViewHolder(Context context, View itemView {
return new ViewHolder(context, itemView);
}
//根據(jù)layoutId 創(chuàng)建ViewHolder
public static ViewHolder createViewHolder(Context context,
ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
return new ViewHolder(context, itemView);
}
/**
* 通過(guò)viewId獲取控件
*
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId)
{
//查找集合中是否存在子View
View view = mViews.get(viewId);
//不存在,執(zhí)行findViewById操作
if (view == null)
{
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getConvertView()
{
return mConvertView;
}
/****以下為輔助方法,可以快速的使用*****/
public ViewHolder setText(int viewId, String text)
{
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public ViewHolder setImageResource(int viewId, int resId)
{
ImageView view = getView(viewId);
view.setImageResource(resId);
return this;
}
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap)
{
ImageView view = getView(viewId);
view.setImageBitmap(bitmap);
return this;
}
public ViewHolder setImageDrawable(int viewId, Drawable drawable)
{
ImageView view = getView(viewId);
view.setImageDrawable(drawable);
return this;
}
/**
* 為ImageView設(shè)置網(wǎng)絡(luò)url 或者 文件path 圖片
* @param viewId View的id
* @param path url或者 文件path
* @param errorId 錯(cuò)誤圖片id
* @param placeId 占位圖片id
* @return
*/
public ViewHolder setImageBUrlOrFile(int viewId,String path,int errorId,int placeId){
ImageView imageView = getView(viewId);
MyImageLoader.getInstance().displayImage(mContext,path,imageView,errorId,placeId);
return this;
}
public ViewHolder setImageNoPlace(int viewId,String path){
ImageView imageView = getView(viewId);
MyImageLoader.getInstance().displayImage(mContext,path,imageView);
return this;
}
public ViewHolder setBackgroundColor(int viewId, int color)
{
View view = getView(viewId);
view.setBackgroundColor(color);
return this;
}
public ViewHolder setBackgroundRes(int viewId, int backgroundRes)
{
View view = getView(viewId);
view.setBackgroundResource(backgroundRes);
return this;
}
public ViewHolder setTextColor(int viewId, int textColor)
{
TextView view = getView(viewId);
view.setTextColor(textColor);
return this;
}
public ViewHolder setTextColorRes(int viewId, int textColorRes)
{
TextView view = getView(viewId);
view.setTextColor(mContext.getResources().getColor(textColorRes));
return this;
}
public ViewHolder setVisible(int viewId, boolean visible)
{
View view = getView(viewId);
view.setVisibility(visible ? View.VISIBLE : View.GONE);
return this;
}
public ViewHolder setTag(int viewId, Object tag)
{
View view = getView(viewId);
view.setTag(tag);
return this;
}
public ViewHolder setTag(int viewId, int key, Object tag)
{
View view = getView(viewId);
view.setTag(key, tag);
return this;
}
public ViewHolder setChecked(int viewId, boolean checked)
{
Checkable view = (Checkable) getView(viewId);
view.setChecked(checked);
return this;
}
/****關(guān)于點(diǎn)擊事件和長(zhǎng)按事件*****/
public ViewHolder setOnClickListener(int viewId,
View.OnClickListener listener)
{
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
public ViewHolder setOnLongClickListener(int viewId,
View.OnLongClickListener listener)
{
View view = getView(viewId);
view.setOnLongClickListener(listener);
return this;
}
}
通過(guò)createViewHolder方法來(lái)實(shí)例化ViewHolder 對(duì)象,實(shí)現(xiàn)了Context 、ConvertView、SparseArray的初始化
在需要操作子View 的時(shí)候,通過(guò)getView 方法來(lái)實(shí)例化子View對(duì)象
在ViewHolder中還封裝了一些常用控件的操作方法,注意到 很多方法返回的是ViewHolder對(duì)象,使得代碼能夠鏈?zhǔn)秸{(diào)用,類(lèi)似于Builder模式的代碼
使用起來(lái)如下:
holder.setText(R.id.tv_title,"Title")
.setText(R.id.tv_content,"Content");
?
Adapter 的封裝
有了ViewHolder 的封裝,回顧需要重寫(xiě)的幾個(gè)方法,接下來(lái)對(duì)Adapter進(jìn)行封裝
public abstract class SingleAdapter<T> extends RecyclerView.Adapter<ViewHolder>{
protected Context mContext;
protected int mLayoutId;
protected List<T> mDatas;
public SingleAdapter(Context context, int layoutId, List<T> datas){
mContext = context;
mLayoutId = layoutId;
mDatas = datas;
}
@Override
public int getItemCount(){
return mDatas==null?0:mDatas.size();
}
@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType){
//創(chuàng)建ViewHolder對(duì)象
return ViewHolder.get(mContext, parent, mLayoutId);;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position){
//綁定數(shù)據(jù)交由子類(lèi)來(lái)完成
convert(holder, mDatas.get(position));
}
public abstract void convert(ViewHolder holder, T t);
//以下是一些公共方法的封裝
public void clearData() {
mDatas.clear();
notifyItemRangeRemoved(0, mDatas.size());
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
if (position != mDatas.size())
notifyItemRangeChanged(position,mDatas.size() - position);
}
public void addData(List<T> list) {
addData(0, list);
}
public void addData(int position, List<T> data) {
if (data != null && data.size() > 0) {
mDatas.addAll(position, data);
notifyItemRangeInserted(position, data.size());
}
}
public void setData(List<T> data) {
if (list != null) {
mDatas = data;
notifyDataSetChanged();
}
}
}
通過(guò)構(gòu)造函數(shù) 初始化Conetext ,Data ,layoutId屬性
getItemCount()方法返回Data 的數(shù)量,并進(jìn)行了判空
onCreateViewHolder()方法通過(guò)ViewHolder 的靜態(tài)方法初始化ViewHolder
onBindViewHolder()綁定數(shù)據(jù)是子類(lèi)來(lái)完成的,所以抽象出convert(ViewHolder holder, T t)方法傳遞參數(shù)
子類(lèi)通過(guò)該方法完成數(shù)據(jù)的綁定
使用
SingleAdapter mSingleAdapter = new SingleAdapter<String>(this,R.layout.item_single){
? @Override
? public void convert(ViewHolder holder, String s){
? holder.setText(R.id.tv_title,s);
}
}