RecyclerViewの通用适配(Adapter)
BaseQuickAdapter 导入(依赖)蓝奏云下载源码 密码:p5j0,添加 library 库到你项目当中BaseQuickAdapter 使用
mRecyclerView.setAdapter(mAdapter=new BaseQuickAdapter<String, BaseViewHolder>(R.layout.rv_item, getItemDatas()) { @Override protected void convert(final BaseViewHolder helper, String item) { helper.setText(R.id.tv_item_text, item); }});
Item点击,长按事件
//Item 点击事件setOnItemClickListener(int viewId, AdapterView.OnItemClickListener listener)//长按事件setOnItemLongClickListener(int viewId, AdapterView.OnItemLongClickListener listener)
子控件的点击事件
helper.setOnClickListener(R.id.tv_item_text, new View.OnClickListener() { @Override public void onClick(View v) { //事件处理 } });
添加头部,尾部
//头部 mAdapter.addHeaderView(); //尾部 mAdapter.addFooterView();
以下是添加头部的代码:
View headerView=getLayoutInflater().inflate(R.layout.rv_header, null);headerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT));mAdapter.addHeaderView(headerView);
尾部的添加与头部的添加类似。添加 Item动画
//一行代码开启动画 默认CUSTOM动画 mAdapter.openLoadAnimation(BaseQuickAdapter.CUSTOMIN);
更换其他动画:
//ALPHAIN, SCALEIN, SLIDEIN_BOTTOM, SLIDEIN_LEFT, SLIDEIN_RIGHT, //SLIDEIN_LEFT_RIGHT, SLIDEIN_BOTTOM_TOP, CUSTOMINmAdapter.openLoadAnimation(BaseQuickAdapter.CUSTOMIN);
自定义 Item动画:
mAdapter.openLoadAnimation(new BaseAnimation[]{ new BaseAnimation() { @Override public Animator[] getAnimators(View view) { return new Animator[]{ObjectAnimator.ofFloat(view, "alpha", 0.5f, 1.0f), ObjectAnimator.ofFloat(view, "scaleX", 0.5f, 1.0f)}; } }});
设置加载更多只需要设置加载更多接口,就可以实现加载更多功能。
mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override public void onLoadMoreRequested() { } });
以下代码正在加载中,加载失败点击重试,加载完成等状态:
mShowType++; if (mShowType==2) { mHandler.postDelayed(new Runnable() { @Override public void run() { //加载失败,点击重试 mAdapter.loadMoreFail(); } }, DELAY_MILLIS); } else if (mShowType >=4) { mHandler.post(new Runnable() { @Override public void run() { //加载完成 mAdapter.loadMoreEnd(); } }); } else { mHandler.postDelayed(new Runnable() { @Override public void run() { mAdapter.addData(addDatas()); //加载更多完成 mAdapter.loadMoreComplete(); } }, DELAY_MILLIS); }
可以设置加载更多类型,只需要添加一行代码,实现不同的加载更多动画:
//设置加载更多 loadingview setLoadMoreType(@LoadMoreType int loadMoreType)
这里有四种模式供你选择,APAY, BALL_BEAT, BALL_CLIP_ROTATE, BALL_SCALE,如果都不满足你的需求,你可以继承 BaseIndicator类从新加载视图。添加空布局
View emptyView=getLayoutInflater().inflate(R.layout.rv_empty, null); emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); //添加空视图 mAdapter.setEmptyView(emptyView); emptyView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "your click empty", Snackbar.LENGTH_SHORT).show(); } });
拖拽和侧滑
添加以下代码:
ItemTouchHelper.Callback callback=new SimpleItemTouchHelperCallback(mAdapter);ItemTouchHelper mItemTouchHelper=new ItemTouchHelper(callback);mAdapter.setItemTouchHelper(mItemTouchHelper);mAdapter.setDragViewId(R.id.iv_drag);mItemTouchHelper.attachToRecyclerView(mRecyclerView);
支持不同类型
mRecyclerView.setAdapter(mAdapter=new BaseMultiItemAdapter<MultiItem>(this, getMultiItemDatas()) { @Override protected void convert(BaseViewHolder helper, MultiItem item) { switch (helper.getItemViewType()) { case MultiItem.SEND: helper.setText(R.id.chat_from_content, item.content); //helper.setImageBitmap(R.id.chat_from_icon,getRoundCornerBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.chat_head), 16)); break; case MultiItem.FROM: helper.setText(R.id.chat_send_content, item.content); //helper.setImageBitmap(R.id.chat_send_icon,getRoundCornerBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.from_head), 16)); break; } } @Override protected void addItemLayout() { addItemType(MultiItem.SEND, R.layout.chat_send_msg); addItemType(MultiItem.FROM, R.layout.chat_from_msg); }});mAdapter.openLoadAnimation(true);btnSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MultiItem multiItem=new MultiItem(); multiItem.itemType=MultiItem.SEND; multiItem.content=etChat.getText().toString(); mAdapter.add(multiItem); mRecyclerView.smoothScrollToPosition(mAdapter.getItemCount()); //mRecyclerView.scrollToPosition(mAdapter.getItemCount() - 1); }});
数据源:
public static List<MultiItem> getMultiItemDatas() { List<MultiItem> list=new ArrayList<>(); for (int i=0; i < 100; i++) { MultiItem multiItem=new MultiItem(); if (i % 2==0) { multiItem.itemType=MultiItem.SEND; multiItem.content="海,妹子约吗"; } else { multiItem.itemType=MultiItem.FROM; multiItem.content="大哥,你别怕"; } list.add(multiItem); } return list;}
流式布局一行代码实现流式布局效果:
mRecyclerView.setLayoutManager(new FlowLayoutManager());
探探翻牌
类似流式效果,重写 LayoutManager 实现翻牌效果:
mRecyclerView.setLayoutManager(new OverLayCardLayoutManager());//添加 itemTouchHelperfinal TanTanCallback callback=new TanTanCallback(mRecyclerView, mAdapter, mAdapter.getData()); //测试竖直滑动是否已经不会被移除屏幕 //callback.setHorizontalDeviation(Integer.MAX_VALUE); final ItemTouchHelper itemTouchHelper=new ItemTouchHelper(callback); itemTouchHelper.attachToRecyclerView(mRecyclerView);
淘宝商品列表切换
//线性 if (mIsLinearManager) { mAdapter.setLayoutType(BaseQuickAdapter.TRANS_0_VIEW); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); } else { //网格 mAdapter.setLayoutType(BaseQuickAdapter.TRANS_1_VIEW); mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2)); //需要显示加载更多则加上下面这句 从新关联recycler mAdapter.onAttachedToRecyclerView(mRecyclerView); } mIsLinearManager=!mIsLinearManager;
注意:最多支持3种不同类型的转换。
单选
实现单选,有好几位同仁都在问我,其实实现单选是比较简单的,方案也比较多,我这里就不在细讲了。下面我给出一种实现方案:
public void setItemChecked(int position) { if (mLastCheckedPosition==position) return; mBooleanArray.put(position, true); if (mLastCheckedPosition > -1) { mBooleanArray.put(mLastCheckedPosition, false); mAdapter.notifyItemChanged(mLastCheckedPosition); } mAdapter.notifyDataSetChanged(); mLastCheckedPosition=position; }
mBooleanArray 存储是否被点击,把点击前的索引值置为 false , 点击索引置为 true 。
XCoreRecyclerAdapter:一种通用的Adapter For RecyclerView
背景
每当我们使用RecyclerView写一个列表的时候,都需要写类似的如下代码:
...
mTestRv=(RecyclerView) view.findViewById(R.id.test_home_rv);
mTestRecyclerAdapter=new TestRecyclerAdapter(context);
mLinearLayoutManager=new LinearLayoutManager(context);
mTestRv.setLayoutManager(mLinearLayoutManager);
mTestRv.setAdapter(mTestRecyclerAdapter);
...
那么,我们一般必不可少的是写一个Adapter,比如,为了支持多种类型,我们需要重写getItemViewType、onCreateViewHolder、onBindViewHolder等方法。比如:
public class TestRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {
... @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=null; switch (viewType) { case Type.TYPE_HEADER:
view=LayoutInflater.from(mContext).inflate(R.layout.card_header,
parent, false); return new HeaderViewHolder(view); case Type.TYPE_HIS_HEAD:
view=LayoutInflater.from(mContext).inflate(R.layout.his_header,
parent, false); return new HeaderHisViewHolder(view);
... default: break;
} return new HeaderViewHolder(new RelativeLayout(mContext));
} @Override
public void onBindViewHolder(ViewHolder holder, int position) { switch (getItemViewType(position)) { case Type.TYPE_HEADER:
HeaderViewHolder headerViewHolder=(HeaderViewHolder) holder;
headerViewHolder.bindView(mDataSet.get(position)); break; case Type.TYPE_HIS_HEAD:
HeaderHisViewHolder headerHisViewHolder=(HeaderHisViewHolder) holder;
headerHisViewHolder.bindView(mDataSet.get(position), mOnHeaderItemClickListener); break;
... default: break;
}
} @Override
public int getItemViewType(int position) {
Data data=mDateSet.get(position);
String type=data.getType(); if("xxx".equals(type)){ return Type.TYPE_HEADER;
}else if("xxxxx".equals(type)){ return Type.TYPE_HIS_HEAD;
}else if(){
...
}
...
}
}
按照上述写法,每当我们需要增加一种类型的时候,都需要修改TestRecyclerAdapter的代码,在onCreateViewHolder等方法中添加对应的代码。后来大家觉得这个挺烦的,然后就把onCreateViewHolder中的switch语句放到工厂类当中,这一定层度上缓解了修改Adapter的频率。那么,我们能不能写一个相对通用的Adapter呢?
封装通用的插件式Adapter
我们希望做到如下目标:
1).Adapter通用化,无需每次新建Adapter
2).Item(Cell)的组件是插件式的,解耦,并可复用
用代码描述为:
...
//创建通用的Adapter
mXCoreRecyclerAdapter=new XCoreRecyclerAdapter(context); //Adapter注册item组件
mXCoreRecyclerAdapter.registerItemUIComponent(new TodoItemComponent()) .registerItemUIComponent(new TestItemComponent()); //设置数据源,完成展示
mXCoreRecyclerAdapter.setDataSet(List<IDataComponent> dataSet) ...
插件式的通用XCoreRecyclerAdapter的原理图如下:
每当我们需要添加一个新的类型时,只需要一下几步:
1.新建一个TodoItemUIComponent组件,继承自XCoreItemUIComponent
实现对应的方法;
2.注册新增的组件TodoItemUIComponent
调用XCoreRecyclerAdapter的registerItemUIComponent方法即可注册。
3.数据源实现IDataComponent接口
getViewType和组件TodoItemUIComponent的getViewType匹配即可;
这样,每次新增一种新的类型的Item时,无需修改Adapter的代码。并且,Item的组件是可复用的。
那么具体怎么设计的呢?
数据源
首先对数据源进行抽象:在Adapter列表中,需要根据数据源Data的不同类型,选择不同的ViewHolder。所以,数据源必须有一个getType的方法。
/**
* 数据源必须实现的接口
*/
public interface IDataComponent { String getViewType();
}
Item组件 ItemUIComponent
ItemUIComponent组件负责某一类type的管理。为了做到插件式,它必须实现的方法有:
onCreateView方法
该方法是用于生成Item的根View的。当Adapter调用onCreateViewHolder时,该方法会被调用。注意,子View的初始化,不要写在onCreateView中。需要写在onViewCreated方法中。
onViewCreated方法
创建完ItemComponent之后,就会立即回调该方法,View的初始化请写在这里。
getViewType方法
该方法是为了和数据源进行关联的,数据源的getViewType方法和组件的getViewType方法的值一致时,即完成匹配。
bindView方法
该方法会在Adapter回调onBindViewHolder
public class TodoItemComponent extends XCoreItemUIComponent { @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container) { //TODO 在这里指定item组件的布局
return inflater.inflate(R.layout.todo_item_layout,container,false);
} @Override
public void onViewCreated(View view) { //TODO 在这里写View初始化
view.findViewById();
...
} @Override
public String getViewType() { //与数据源的getViewType关联
return TodoItemComponent.class.getSimpleName();
} @Override
public void bindView(IXCoreComponent coreComponent,
XCoreRecyclerAdapter coreRecyclerAdapter,
XCoreRecyclerAdapter.IDataComponent data, int pos) { //TODO 在这里写绑定逻辑
}
}
数据源和Item组件都已经定义好,那么,核心的XCoreRecyclerAdapter源码应该怎么做呢?
XCoreRecyclerAdapter源码
package com.github.nuptboyzhb.xcore.adapter;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.text.TextUtils;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import com.github.nuptboyzhb.xcore.components.IXCoreComponent;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/**
* @version mochuan.zhb on 2016/8/9.
* @Author Zheng Haibo
* @Blog github.com/nuptboyzhb
* @Company Alibaba Group
* @Description 通用的Adapter for RecyclerView
*/public class XCoreRecyclerAdapter extends RecyclerView.Adapter<XCoreRecyclerAdapter.CommonViewHolder> { private IXCoreComponent mIXCoreComponent;//外层UI组件
private List<IDataComponent> mDataSet=new ArrayList<IDataComponent>();//数据源
private SparseArray<XCoreItemUIComponent> mConfigurationSparseArray=new SparseArray<XCoreItemUIComponent>();//集合:type对应的Item组件
private Map<String, Integer> mViewTypeMap=new HashMap<String, Integer>();//type的string和int映射
public XCoreRecyclerAdapter() {
} public XCoreRecyclerAdapter(IXCoreComponent mIXCoreComponent) { this.mIXCoreComponent=mIXCoreComponent;
} @Override
public CommonViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) { //根据数据类型获取对应的item组件
XCoreItemUIComponent XCoreItemUIComponent=mConfigurationSparseArray.get(type); if (XCoreItemUIComponent==null) {//如果未获取到,展示空item
return getDefaultViewHolder(viewGroup.getContext());
} try { //使用item组件创建一个新的View
View view=XCoreItemUIComponent.onCreateView(LayoutInflater.from(viewGroup.getContext()), viewGroup); //使用View构建内部的ViewHolder
CommonViewHolder commonViewHolder=new CommonViewHolder(view); //创建一个新的Item组件
XCoreItemUIComponent realItem=XCoreItemUIComponent.getClass().newInstance(); //将创建的View设置到真是的Item组件中
realItem.setItemView(view); //使用内部ViewHolder
commonViewHolder.setXCoreItemUIComponent(realItem); return commonViewHolder;
} catch (Throwable t) {
t.printStackTrace();
} return getDefaultViewHolder(viewGroup.getContext());
} @Override
public void onBindViewHolder(CommonViewHolder baseViewHolder, int pos) {
baseViewHolder.bindView(mIXCoreComponent, this, mDataSet.get(pos), pos);
} @Override
public int getItemViewType(int position) {
IDataComponent item=mDataSet.get(position);
Integer integer=mViewTypeMap.get(item.getViewType()); if (integer==null) { return -1;
} return integer;
} @Override
public int getItemCount() { if (mDataSet !=null) { return mDataSet.size();
} return 0;
} /**
* 获取Adapter的数据源
*
* @return
*/
public List<IDataComponent> getDataSet() { return mDataSet;
} /**
* 设置Adapter的数据源
*
* @param dataSet
*/
public void setDataSet(List<IDataComponent> dataSet) { this.mDataSet=dataSet;
notifyDataSetChanged();
} /**
* get the error view holder
*
* @param context
* @return
*/
protected CommonViewHolder getDefaultViewHolder(Context context) { return new CommonViewHolder(new View(context));
} /**
* get the unique int type
*
* @param name
* @return
*/
private int getUniqueIntType(String name) { if (TextUtils.isEmpty(name)) { return -1;
} int type=name.hashCode(); while (true) {
XCoreItemUIComponent old=mConfigurationSparseArray.get(type); if (old !=null) {
String oldName=old.getViewType(); if (!name.equals(oldName)) {
type=type + 1;
} else { return type;
}
} else { return type;
}
}
} /**
* 注册Item组件
*
* @param XCoreItemUIComponent
* @return
*/
public XCoreRecyclerAdapter registerItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) { if (XCoreItemUIComponent==null || TextUtils.isEmpty(XCoreItemUIComponent.getViewType())) { return this;
} int viewTypeInt=getUniqueIntType(XCoreItemUIComponent.getViewType());
mViewTypeMap.put(XCoreItemUIComponent.getViewType(), viewTypeInt);
mConfigurationSparseArray.put(viewTypeInt, XCoreItemUIComponent); return this;
} /**
* 注销配置
*
* @param XCoreItemUIComponent
* @return
*/
public XCoreRecyclerAdapter unregisterItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) { if (XCoreItemUIComponent==null || TextUtils.isEmpty(XCoreItemUIComponent.getViewType())) { return this;
} int index=mConfigurationSparseArray.indexOfValue(XCoreItemUIComponent); if (index==-1) { return this;
}
mConfigurationSparseArray.remove(index); return this;
} /**
* 数据源必须实现的接口
*/
public interface IDataComponent { String getViewType();
} /**
* 使用CommonViewHolder代理XCoreItemUIComponent组件
*/
public static class CommonViewHolder extends RecyclerView.ViewHolder { private XCoreItemUIComponent XCoreItemUIComponent; public void setXCoreItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) { this.XCoreItemUIComponent=XCoreItemUIComponent;
} public XCoreItemUIComponent getXCoreItemUIComponent() { return XCoreItemUIComponent;
} public CommonViewHolder(View itemView) { super(itemView);
} public void bindView(IXCoreComponent mIXCoreComponent,
XCoreRecyclerAdapter XCoreRecyclerAdapter,
XCoreRecyclerAdapter.IDataComponent data, int pos) { if (XCoreItemUIComponent==null) { return;
}
XCoreItemUIComponent.bindView(mIXCoreComponent, XCoreRecyclerAdapter, data
, pos);
} public void onViewDetachedFromWindow() { if (XCoreItemUIComponent !=null) {
XCoreItemUIComponent.onViewDetachedFromWindow();
}
}
} @Override
public void onViewDetachedFromWindow(CommonViewHolder holder) { super.onViewDetachedFromWindow(holder);
holder.onViewDetachedFromWindow();
}
}
XCoreItemUIComponent源码
package com.github.nuptboyzhb.xcore.adapter;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.github.nuptboyzhb.xcore.components.IXCoreComponent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @version mochuan.zhb on 2016/8/12.
* @Author Zheng Haibo
* @Blog github.com/nuptboyzhb
* @Company Alibaba Group
* @Description item component 抽象类
*/
public abstract class XCoreItemUIComponent implements IXCoreComponent {
/**
* 创建View *
* @param inflater
* @param container
* @return
*/
public abstract View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container);
/**
* 在此做View的初始化操作 *
* @param view
*/
public abstract void onViewCreated(View view);
/**
* item组件支持的ViewType,与IDataComponent的getViewType对应 *
* @return
*/
public abstract String getViewType();
/**
* 绑定数据-频繁回调 *
* @param coreComponent 外出UI组件
* @param coreRecyclerAdapter Adapter
* @param data 对应的数据源
* @param pos item所在列表的位置
*/
public abstract void bindView(IXCoreComponent coreComponent,
XCoreRecyclerAdapter coreRecyclerAdapter,
XCoreRecyclerAdapter.IDataComponent data,
int pos);
/**
* item组件销毁时调用 */
public abstract void onViewDetachedFromWindow();
//必须有无参数的构造函数
public View itemView;
public void setItemView(View itemView) {
this.itemView=itemView;
onViewCreated(itemView);
}
}
后续
本篇博客主要是介绍通用的XCoreRecyclerAdapter引擎,极大提高了Adapter的复用率,让开发集中把精力集中在必要的业务代码上,提高开发效率。另外,与XCoreRecyclerAdapter引擎相配套的还有UI组件化、数据控制框架XCoreRedux、数据绑定等。请参考下一遍博客:《Android Redux实践与UI组件化:XCoreRedux框架》(ing...)
下一篇:UT怎么下载---
发表评论