RecyclerView Adapter 优雅封装,一个Adapter搞定所有列表
wxpletgo
8年前
<p>项目中,我们用得最多的元素就是列表了,在Android 中,实现列表用原生的RecyclerView就能满足需求,关于RecyclerView 的基础使用这里不做过多的介绍,网上有太多的博文介绍了。本篇文章将介绍自己封装的一个Adapter,帮你快速高效的添加一个列表(包括单 Item 列表和多item列表)。</p> <h3>理念</h3> <p>1, 构造一个通用的Adapter模版,避免每添加一个列表就要写一个Adapter,避免写Adapter中的大量重复代码。</p> <p>2,通过组装的方式来构建Adapter,将每一种(ViewType不同的)Item抽象成一个单独组件,Adapter 就是一个壳,我们只需要向Adapter中添加Item就行,这样做的好处就是减少耦合,去掉一种item 或者添加一种item对于列表是没有任何影响的。</p> <p>3,高内聚,低耦合,扩展方便。</p> <h3>思路</h3> <p>为每一种 viewType 定义一个Cell,Cell就是上面提到的独立组件,它负责创建ViewHolder,数据绑定和逻辑处理。它有2个重要的方法,onCreateViewHolder 负责创建ViewHolder,onBindViewHolder负责数据绑定,这两个方法的定义和生命周期同Adapter种的2个方法一样,事实上,Adapter 中的onCreateViewHolder和onBindViewHolder 最终调用的是Cell中的方法。</p> <p>一种 ViewType 对应一个Cell</p> <p>看一个示例:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/042a2de8c93ed02094562f8e303b9402.png"></p> <p style="text-align:center">cell_simple.png</p> <p>如上图:以豆瓣APP的首页为例,文章包含图片和视频的两个Item 的布局是不同的,因此,可以添加两个Cell(ImageCell和VideoCell)来分别处理这两种Item。</p> <p>有了Cell之后,要向列表添加添加Header和Footer 的需求就很简单了,我们直接添加一个HeaderCell和FooterCell 就可以了,也不用更改Adapter代码,是不是很方便。此外,还可以用Cell实现列表LoadMore(加载更多)状态、Loadding(加载中)状态、Empty(空页面)状态、Error(出错)状态 View的显示。</p> <h3>包结构</h3> <p style="text-align:center"><img src="https://simg.open-open.com/show/8a9724ba1c906838dceb1f11f84ce71b.png"></p> <p style="text-align:center">rv_pakage.png</p> <p>介绍: <strong>1,base:</strong> base包下面为Lib的主要代码,一个Cell接口和三个抽象类,分别抽取了Adapter,ViewHolder,Cell的公共逻辑。</p> <p>2,cell:cell包下面有4个cell,分别显示列表的LoadMore,Loading,Empty,Error状态。</p> <p>3,fragment:有一个Fragment抽象类,定义了一个UI模版(不需要额外添加布局文件),要实现列表的界面只需要继承AbsBaseFragment,实现几个方法添加数据就OK。</p> <h3>具体代码</h3> <p>1,Cell 接口定义</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/1/19. */ public interface Cell { /** * 回收资源 * */ public void releaseResource(); /** * 获取viewType * @return */ public int getItemType(); /** * 创建ViewHolder * @param parent * @param viewType * @return */ public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType); /** * 数据绑定 * @param holder * @param position */ public void onBindViewHolder(RVBaseViewHolder holder, int position); }</code></pre> <p>定义了4个方法,除了回收资源的方法releaseResource(),其它三个和Adapter中的一样。</p> <p>2,RVBaseCell</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/1/19. */ public abstract class RVBaseCell<T> implements Cell { public RVBaseCell(T t){ mData = t; } public T mData; @Override public void releaseResource() { // do nothing // 如果有需要回收的资源,子类自己实现 } }</code></pre> <p>抽象类,接受一个范型T(Cell接受的数据实体),实现了releaseResource方法,但什么事也没干,因为有很多简单的Cell没有资源回收,就不需要实现。如果子类Cell 有资源回收,重写这个方法就可以了。</p> <p>3, RVBaseViewHolder</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/1/19. */ public class RVBaseViewHolder extends RecyclerView.ViewHolder{ private SparseArray<View> views; private View mItemView; public RVBaseViewHolder(View itemView) { super(itemView); views = new SparseArray<>(); mItemView = itemView; } /** * 获取ItemView * @return */ public View getItemView() { return mItemView; } public View getView(int resId) { return retrieveView(resId); } public TextView getTextView(int resId){ return retrieveView(resId); } public ImageView getImageView(int resId){ return retrieveView(resId); } public Button getButton(int resId){ return retrieveView(resId); } @SuppressWarnings("unchecked") protected <V extends View> V retrieveView(int viewId){ View view = views.get(viewId); if(view == null){ view = mItemView.findViewById(viewId); views.put(viewId,view); } return (V) view; } public void setText(int resId,CharSequence text){ getTextView(resId).setText(text); } public void setText(int resId,int strId){ getTextView(resId).setText(strId); } }</code></pre> <p>以前写Adapter的时候,每一种viewType 都对应了一个ViewHolder,其中有大量的findViewById绑定视图,有了RVBaseViewHolder,再也不需要定义ViewHolder了,通过id获取View就行,View用SparseArray 保存进行了复用,避免每一次都find。</p> <p>4,RVBaseAdapter</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/1/19. */ public abstract class RVBaseAdapter<C extends RVBaseCell> extends RecyclerView.Adapter<RVBaseViewHolder>{ public static final String TAG = "RVBaseAdapter"; protected List<C> mData; public RVBaseAdapter(){ mData = new ArrayList<>(); } public void setData(List<C> data) { addAll(data); notifyDataSetChanged(); } public List<C> getData() { return mData; } @Override public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { for(int i=0;i<getItemCount();i++){ if(viewType == mData.get(i).getItemType()){ return mData.get(i).onCreateViewHolder(parent,viewType); } } throw new RuntimeException("wrong viewType"); } @Override public void onBindViewHolder(RVBaseViewHolder holder, int position) { mData.get(position).onBindViewHolder(holder,position); } @Override public void onViewDetachedFromWindow(RVBaseViewHolder holder) { super.onViewDetachedFromWindow(holder); Log.e(TAG,"onViewDetachedFromWindow invoke..."); //释放资源 int position = holder.getAdapterPosition(); //越界检查 if(position<0 || position>=mData.size()){ return; } mData.get(position).releaseResource(); } @Override public int getItemCount() { return mData == null ? 0:mData.size(); } @Override public int getItemViewType(int position) { return mData.get(position).getItemType(); } /** * add one cell * @param cell */ public void add(C cell){ mData.add(cell); int index = mData.indexOf(cell); notifyItemChanged(index); } public void add(int index,C cell){ mData.add(index,cell); notifyItemChanged(index); } /** * remove a cell * @param cell */ public void remove(C cell){ int indexOfCell = mData.indexOf(cell); remove(indexOfCell); } public void remove(int index){ mData.remove(index); notifyItemRemoved(index); } /** * * @param start * @param count */ public void remove(int start,int count){ if((start +count) > mData.size()){ return; } int size = getItemCount(); for(int i =start;i<size;i++){ mData.remove(i); } notifyItemRangeRemoved(start,count); } /** * add a cell list * @param cells */ public void addAll(List<C> cells){ if(cells == null || cells.size() == 0){ return; } Log.e("zhouwei","addAll cell size:"+cells.size()); mData.addAll(cells); notifyItemRangeChanged(mData.size()-cells.size(),mData.size()); } public void addAll(int index,List<C> cells){ if(cells == null || cells.size() == 0){ return; } mData.addAll(index,cells); notifyItemRangeChanged(index,index+cells.size()); } public void clear(){ mData.clear(); notifyDataSetChanged(); } /** * 如果子类需要在onBindViewHolder 回调的时候做的操作可以在这个方法里做 * @param holder * @param position */ protected abstract void onViewHolderBound(RVBaseViewHolder holder, int position); }</code></pre> <p>RVBaseAdapter 继承 RecyclerView.Adapter,接受的是RVBaseCell类型,保存了一个Cell 列表。其中还有有添加、移除,清空、更新数据的方法。</p> <p>注意其中几个方法:</p> <p>1,getItemViewType:调用的是对应position Cell 的getItemViewType 方法。</p> <p>2,onCreateViewHolder:调用Cell 的onCreateViewHolder 创建ViewHolder。</p> <p>3,onBindViewHolder:调用对应Cell的onBindViewHolder 方法绑定数据</p> <p>4,onViewDetachedFromWindow:资源回收</p> <p>5,RVSimpleAdapter</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/1/23. */ public class RVSimpleAdapter extends RVBaseAdapter{ public static final int ERROR_TYPE = Integer.MAX_VALUE -1; public static final int EMPTY_TYPE = Integer.MAX_VALUE -2; public static final int LOADING_TYPE = Integer.MAX_VALUE -3; public static final int LOAD_MORE_TYPE = Integer.MAX_VALUE -4; private EmptyCell mEmptyCell; private ErrorCell mErrorCell; private LoadingCell mLoadingCell; private LoadMoreCell mLoadMoreCell; //LoadMore 是否已显示 private boolean mIsShowLoadMore = false; public RVSimpleAdapter(){ mEmptyCell = new EmptyCell(null); mErrorCell = new ErrorCell(null); mLoadingCell = new LoadingCell(null); mLoadMoreCell = new LoadMoreCell(null); } @Override protected void onViewHolderBound(RVBaseViewHolder holder, int position) { } @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); //处理GridView 布局 if(manager instanceof GridLayoutManager){ final GridLayoutManager gridLayoutManager = (GridLayoutManager) manager; gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { int viewType = getItemViewType(position); return (viewType == ERROR_TYPE|| viewType == EMPTY_TYPE || viewType == LOADING_TYPE ||viewType == LOAD_MORE_TYPE) ? gridLayoutManager.getSpanCount():1; } }); } } @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { // 处理StaggeredGridLayoutManager 显示这个Span int position = holder.getAdapterPosition(); int viewType = getItemViewType(position); if(isStaggeredGridLayout(holder)){ if(viewType == ERROR_TYPE|| viewType == EMPTY_TYPE || viewType == LOADING_TYPE ||viewType == LOAD_MORE_TYPE){ StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); //设置显示整个span params.setFullSpan(true); } } } private boolean isStaggeredGridLayout(RecyclerView.ViewHolder holder) { ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams(); if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) { return true; } return false; } /** * 显示LoadingView * <p>请求数据时调用,数据请求完毕时调用{@link #hideLoading }</p> * @see #showLoadingKeepCount(int) */ public void showLoading(){ clear(); add(mLoadingCell); } public void showLoading(View loadingView){ if(loadingView == null){ showLoading(); } clear(); mLoadingCell.setLoadingView(loadingView); add(mLoadingCell); } /** * 显示LoadingView * <p>列表显示LoadingView并保留keepCount个Item</p> * @param keepCount 保留的条目数量 */ public void showLoadingKeepCount(int keepCount){ if(keepCount < 0 || keepCount>mData.size()){ return; } remove(keepCount,mData.size() - keepCount); if(mData.contains(mLoadingCell)){ mData.remove(mLoadingCell); } add(mLoadingCell); } /** * hide Loading view */ public void hideLoading(){ if(mData.contains(mLoadingCell)){ mData.remove(mLoadingCell); } } /** * 显示错误提示 * <p>当网络请求发生错误,需要在界面给出错误提示时,调用{@link #showError}</p> * @see #showErrorKeepCount(int) */ public void showError(){ clear(); add(mErrorCell); } /** * 显示错误提示 * <p>当网络请求发生错误,需要在界面给出错误提示时,调用{@link #showErrorKeepCount(int)},并保留keepCount 条Item</p> * @param keepCount 保留Item数量 */ public void showErrorKeepCount(int keepCount){ if(keepCount < 0 || keepCount>mData.size()){ return; } remove(keepCount,mData.size() - keepCount); if(mData.contains(mErrorCell)){ mData.remove(mErrorCell); } add(mErrorCell); } /** * 隐藏错误提示 */ public void hideErorr(){ if(mData.contains(mErrorCell)){ remove(mErrorCell); } } /** * 显示LoadMoreView * <p>当列表滑动到底部时,调用{@link #showLoadMore()} 提示加载更多,加载完数据,调用{@link #hideLoadMore()} * 隐藏LoadMoreView,显示列表数据。</p> * */ public void showLoadMore(){ if(mData.contains(mLoadMoreCell)){ return; } add(mLoadMoreCell); mIsShowLoadMore = true; } /** * 隐藏LoadMoreView * <p>调用{@link #showLoadMore()}之后,加载数据完成,调用{@link #hideLoadMore()}隐藏LoadMoreView</p> */ public void hideLoadMore(){ if(mData.contains(mLoadMoreCell)){ remove(mLoadMoreCell); mIsShowLoadMore = false; } } /** * LoadMore View 是否已经显示 * @return */ public boolean isShowLoadMore() { return mIsShowLoadMore; } /** * * @param keepCount */ public void showEmptyKeepCount(int keepCount){ if(keepCount < 0 || keepCount>mData.size()){ return; } remove(keepCount,mData.size() - keepCount); if(mData.contains(mEmptyCell)){ mData.remove(mEmptyCell); } add(mEmptyCell); } /** * 显示空view * <p>当页面没有数据的时候,调用{@link #showEmpty()}显示空View,给用户提示</p> */ public void showEmpty(){ clear(); add(mEmptyCell); } /** * 隐藏空View */ public void hideEmpty(){ if(mData.contains(mEmptyCell)){ remove(mEmptyCell); } } }</code></pre> <p>RVSimpleAdapter 是RVBaseAdapter 的默认实现类,添加了显示LoadMore View、Loading View 、Empty View、ErrorView 的功能。</p> <p>6,AbsBaseFragment</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/2/3. */ public abstract class AbsBaseFragment<T> extends Fragment { protected RecyclerView mRecyclerView; protected RVSimpleAdapter mBaseAdapter; private FrameLayout mToolbarContainer; protected SwipeRefreshLayout mSwipeRefreshLayout; /** * RecyclerView 最后可见Item在Adapter中的位置 */ private int mLastVisiblePosition = -1; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.base_fragment_layout,null); return view; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.base_refresh_layout); mToolbarContainer = (FrameLayout) view.findViewById(R.id.toolbar_container); mRecyclerView = (RecyclerView) view.findViewById(R.id.base_fragment_rv); mRecyclerView.setLayoutManager(initLayoutManger()); mBaseAdapter = initAdapter(); mRecyclerView.setAdapter(mBaseAdapter); mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { setRefreshing(true); onPullRefresh(); } }); mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if(layoutManager instanceof LinearLayoutManager){ mLastVisiblePosition = ((LinearLayoutManager)layoutManager).findLastVisibleItemPosition(); }else if(layoutManager instanceof GridLayoutManager){ mLastVisiblePosition = ((GridLayoutManager)layoutManager).findLastVisibleItemPosition(); }else if(layoutManager instanceof StaggeredGridLayoutManager){ StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; int []lastPositions = new int[staggeredGridLayoutManager.getSpanCount()]; staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions); mLastVisiblePosition = findMax(lastPositions); } } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { View firstView = recyclerView.getChildAt(0); int top = firstView.getTop(); int topEdge = recyclerView.getPaddingTop(); //判断RecyclerView 的ItemView是否满屏,如果不满一屏,上拉不会触发加载更多 boolean isFullScreen = top < topEdge; RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); int itemCount = manager.getItemCount(); //因为LoadMore View 是Adapter的一个Item,显示LoadMore 的时候,Item数量+1了,导致 mLastVisibalePosition == itemCount-1 // 判断两次都成立,因此必须加一个判断条件 !mBaseAdapter.isShowLoadMore() if(newState == RecyclerView.SCROLL_STATE_IDLE && mLastVisiblePosition == itemCount-1 && isFullScreen && !mBaseAdapter.isShowLoadMore()){ //最后一个Item了 mBaseAdapter.showLoadMore(); onLoadMore(); } } }); View toolbarView = addToolbar(); if(toolbarView!=null && mToolbarContainer!=null ){ mToolbarContainer.addView(toolbarView); } onRecyclerViewInitialized(); } /** * hide load more progress */ public void hideLoadMore(){ if(mBaseAdapter!=null){ mBaseAdapter.hideLoadMore(); } } /** * 获取组数最大值 * @param lastPositions * @return */ private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value > max) { max = value; } } return max; } /** * 设置刷新进度条的颜色 * see{@link SwipeRefreshLayout#setColorSchemeResources(int...)} * @param colorResIds */ public void setColorSchemeResources(@ColorRes int... colorResIds){ if(mSwipeRefreshLayout!=null){ mSwipeRefreshLayout.setColorSchemeResources(colorResIds); } } /** * 设置刷新进度条的颜色 * see{@link SwipeRefreshLayout#setColorSchemeColors(int...)} * @param colors */ public void setColorSchemeColors(int... colors){ if(mSwipeRefreshLayout!=null){ mSwipeRefreshLayout.setColorSchemeColors(colors); } } /** * 设置刷新进度条背景色 * see{@link SwipeRefreshLayout#setProgressBackgroundColorSchemeResource(int)} (int)} * @param colorRes */ public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) { if(mSwipeRefreshLayout!=null){ mSwipeRefreshLayout.setProgressBackgroundColorSchemeResource(colorRes); } } /** * 设置刷新进度条背景色 * see{@link SwipeRefreshLayout#setProgressBackgroundColorSchemeColor(int)} * @param color */ public void setProgressBackgroundColorSchemeColor(@ColorInt int color) { if(mSwipeRefreshLayout!=null){ mSwipeRefreshLayout.setProgressBackgroundColorSchemeColor(color); } } /** * Notify the widget that refresh state has changed. Do not call this when * refresh is triggered by a swipe gesture. * * @param refreshing Whether or not the view should show refresh progress. */ public void setRefreshing(boolean refreshing){ if(mSwipeRefreshLayout== null){ return; } mSwipeRefreshLayout.setRefreshing(refreshing); } /** * 子类可以自己指定Adapter,如果不指定默认RVSimpleAdapter * @return */ protected RVSimpleAdapter initAdapter(){ return new RVSimpleAdapter(); } /** * 子类自己指定RecyclerView的LayoutManager,如果不指定,默认为LinearLayoutManager,VERTICAL 方向 * @return */ protected RecyclerView.LayoutManager initLayoutManger(){ LinearLayoutManager manager = new LinearLayoutManager(getContext()); manager.setOrientation(LinearLayoutManager.VERTICAL); return manager; } /** * 添加TitleBar * @param */ public View addToolbar(){ //如果需要Toolbar,子类返回Toolbar View return null; } /** *RecyclerView 初始化完毕,可以在这个方法里绑定数据 */ public abstract void onRecyclerViewInitialized(); /** * 下拉刷新 */ public abstract void onPullRefresh(); /** * 上拉加载更多 */ public abstract void onLoadMore(); /** * 根据实体生成对应的Cell * @param list 实体列表 * @return cell列表 */ protected abstract List<Cell> getCells(List<T> list); }</code></pre> <p>AbsBaseFragment,实现了上拉加载和下拉刷新功能,添加Toolbar等,上拉加载可以自定义View,下拉刷新用的是Google的SwipeRefreshLayout。要添加一个列表界面,只需要继承AbsBaseFragment,实现几个抽象方法添加Cell就行了,非常方便。</p> <h3>用法示例:</h3> <p>添加一个多Item的列表:</p> <p>1,创建一个Fragment继承AbsBaseFragment,实现几个方法。</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/2/3. */ public class HomePageFragment extends AbsBaseFragment<Entry> { @Override public void onRecyclerViewInitialized() { //初始化View和数据加载 } @Override public void onPullRefresh() { //下拉刷新回调 } @Override public void onLoadMore() { //上拉加载回调 } protected List<Cell> getCells(List<Entry> entries){ //根据实体生成Cell return null; } }</code></pre> <p>实现上面几个抽象方法,实际上只实现onRecyclerViewInitialized和getCells两个方法就可以实现列表,其它两个方法是下拉刷新和上拉加载的。</p> <p>2,创建Cell类</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/2/7. */ public class BannerCell extends RVBaseCell<List<String>> { public static final int TYPE = 2; private ConvenientBanner mConvenientBanner; public BannerCell(List<String> strings) { super(strings); } @Override public int getItemType() { return TYPE; } @Override public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new RVBaseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_cell_layoout,null)); } @Override public void onBindViewHolder(RVBaseViewHolder holder, int position) { mConvenientBanner = (ConvenientBanner) holder.getView(R.id.banner); mConvenientBanner.setPages(new CBViewHolderCreator<NetworkImageHolderView>() { @Override public NetworkImageHolderView createHolder() { return new NetworkImageHolderView(); } }, mData); mConvenientBanner.startTurning(2000); } @Override public void releaseResource() { if(mConvenientBanner!=null){ mConvenientBanner.stopTurning(); } } public static class NetworkImageHolderView implements CBPageAdapter.Holder<String>{ private ImageView imageView; @Override public View createView(Context context) { imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return imageView; } @Override public void UpdateUI(Context context, int position, String data) { ImageLoader.getInstance().displayImage(data,imageView); } } }</code></pre> <pre> <code class="language-java">/** * Created by zhouwei on 17/1/19. */ public class ImageCell extends RVBaseCell<Entry> { public static final int TYPE = 1; public ImageCell(Entry entry) { super(entry); } @Override public int getItemType() { return TYPE; } @Override public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new RVBaseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.image_cell_layout,null)); } @Override public void onBindViewHolder(RVBaseViewHolder holder, int position) { Picasso.with(holder.getItemView().getContext()).load(mData.imageUrl).into(holder.getImageView(R.id.image)); } }</code></pre> <pre> <code class="language-java">/** * Created by zhouwei on 17/1/19. */ public class TextCell extends RVBaseCell<Entry> { public static final int TYPE = 0; public TextCell(Entry entry) { super(entry); } @Override public int getItemType() { return TYPE; } @Override public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new RVBaseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.text_cell_layout,null)); } @Override public void onBindViewHolder(RVBaseViewHolder holder, int position) { holder.setText(R.id.text_content,mData.content); } }</code></pre> <p>上面创建了3个Cell,也就是这个列表包含了3种不同类型的Item。</p> <p>注意:一个列表内,每个Cell 的TYPE要不相同,也就是getItemType方法的返回值要不同。</p> <p>3,onRecyclerViewInitialized ,做初始化和加载数据</p> <pre> <code class="language-java">@Override public void onRecyclerViewInitialized() { //初始化View和数据加载 //设置刷新进度条颜色 setColorSchemeResources(R.color.colorAccent); loadData(); } /** * 模拟从服务器取数据 */ private void loadData(){ View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null); mBaseAdapter.showLoading(loadingView); mRecyclerView.postDelayed(new Runnable() { @Override public void run() { mBaseAdapter.hideLoading(); mBaseAdapter.addAll(getCells(mockData())); } },2000); }</code></pre> <p>4,实现getCells方法,生成Cell</p> <pre> <code class="language-java">protected List<Cell> getCells(List<Entry> entries){ //根据实体生成Cell List<Cell> cells = new ArrayList<>(); cells.add(new BannerCell(Arrays.asList(DataMocker.images))); for (int i=0;i<entries.size();i++){ Entry entry = entries.get(i); if(entry.type == Entry.TYPE_IMAGE){ cells.add(new ImageCell(entry)); }else{ cells.add(new TextCell(entry)); } } return cells; }</code></pre> <p>上面根据实体生成不同的Cell。有三种Cell,BannerCell,ImageCell和TextCell。</p> <p>以上4个步骤就能实现一个界面复杂包含多做Item的列表了效果图如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/d70f17edae14913fc150dd874b51b634.gif"></p> <p style="text-align:center">adapter_cell.gif</p> <p>HomePageFragment 的完整代码如下:</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/2/3. */ public class HomePageFragment extends AbsBaseFragment<Entry> { @Override public void onRecyclerViewInitialized() { //初始化View和数据加载 //设置刷新进度条颜色 setColorSchemeResources(R.color.colorAccent); loadData(); } @Override public void onPullRefresh() { //下拉刷新回调 mRecyclerView.postDelayed(new Runnable() { @Override public void run() { setRefreshing(false); } },2000); } @Override public void onLoadMore() { //上拉加载回调 loadMore(); } private void loadMore(){ mRecyclerView.postDelayed(new Runnable() { @Override public void run() { hideLoadMore(); mBaseAdapter.addAll(getCells(mockMoreData())); } },10000); } protected List<Cell> getCells(List<Entry> entries){ //根据实体生成Cell List<Cell> cells = new ArrayList<>(); cells.add(new BannerCell(Arrays.asList(DataMocker.images))); for (int i=0;i<entries.size();i++){ Entry entry = entries.get(i); if(entry.type == Entry.TYPE_IMAGE){ cells.add(new ImageCell(entry)); }else{ cells.add(new TextCell(entry)); } } return cells; } @Override public View addToolbar() { View toolbar = LayoutInflater.from(getContext()).inflate(R.layout.title_bar_layout,null); return toolbar; } /** * 模拟从服务器取数据 */ private void loadData(){ View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null); mBaseAdapter.showLoading(loadingView); mRecyclerView.postDelayed(new Runnable() { @Override public void run() { mBaseAdapter.hideLoading(); mBaseAdapter.addAll(getCells(mockData())); } },2000); } }</code></pre> <p>Grid 列表和瀑布流列表:</p> <p>上面演示了添加多Item type 的列表,添加单Item的列表也是一样的,只不过只有一个Cell而已。添加Grid 列表和瀑布流列表差不多的,只是RecylerView 的LayoutManager不同而已。</p> <p>瀑布流列表示例:</p> <pre> <code class="language-java">/** * Created by zhouwei on 17/2/4. */ public class DetailFragment extends AbsBaseFragment<DetailEntry> { @Override public void onRecyclerViewInitialized() { mBaseAdapter.setData(getCells(mockStaggerData())); } @Override public void onPullRefresh() { } @Override public void onLoadMore() { } @Override protected RecyclerView.LayoutManager initLayoutManger() { StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL); return layoutManager; } @Override protected List<Cell> getCells(List<DetailEntry> list) { List<Cell> cells = new ArrayList<>(); for (int i=0;i<list.size();i++){ cells.add(new DetailCell(list.get(i))); } return cells; } }</code></pre> <p>只需要重写initLayoutManager这个方法,返回一个瀑布流的LayoutMannger就可以了。</p> <p>效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fa39d49879198c7bf48d61cdbab326c3.gif"></p> <p style="text-align:center">stagger_adapter_cell.gif</p> <h3>其它演示示例:加载更多、Loading View 、Error View ,Empty View</h3> <p>1,显示LoadMoreView</p> <p>提供了默认的LoadingView,调用代码如下:</p> <pre> <code class="language-java">mBaseAdapter.showLoadMore();</code></pre> <p>隐藏LoadMore View 调用如下代码:</p> <pre> <code class="language-java">if(mBaseAdapter!=null){ mBaseAdapter.hideLoadMore(); }</code></pre> <p>效果图看上面演示的瀑布流效果图。</p> <p>2,显示loading View</p> <p>提供了默认的Loading View,调用代码如下:</p> <pre> <code class="language-java">mBaseAdapter.showLoading();</code></pre> <p>当然也可以自定义Loading View,提供一个布局即可:</p> <pre> <code class="language-java">View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null); mBaseAdapter.showLoading(loadingView);</code></pre> <p>效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/6fe1fe46041eda192d6ac12007e82de4.png"></p> <p style="text-align:center">loading_view.png</p> <p>还有一种情况是,顶部有一个固定的HeaderCell,不需要加载数据,显示静态页面,下面加载数据时需要Loading态,Error状态,Empty状态等等。提供如下3个方法:</p> <ul> <li> <p>showLoadingKeepCount(int keepCount,int height,View loadingView)</p> <p>列表Loading状态显示的View,保留keepCountg个Item,并指定高度,指定显示的View</p> </li> <li> <p>showLoadingKeepCount(int keepCount,int height)</p> <p>列表Loading状态显示的View,保留keepCountg个Item,并指定高度(显示的是提供的默认Loading View)</p> </li> <li> <p>showLoadingKeepCount(int keepCount)</p> <p>显示默认LoadingView</p> </li> </ul> <p>使用代码如下:</p> <pre> <code class="language-java">View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null); mBaseAdapter.showLoadingKeepCount(1,height,loadingView);</code></pre> <p>效果图如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fc96facecec89fa974f6a5353b010ff2.png"></p> <p style="text-align:center">loading_view_keep_count.png</p> <p>隐藏Loading View 调用对应hide 方法:</p> <pre> <code class="language-java">mBaseAdapter.hideLoading();</code></pre> <p>3, Error View 和 Empty View</p> <p>显示Error View 和Empty View 与Loading View 的显示与隐藏是一样,不在过多讲,直接去看源码,提供了几个方法:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/12e55e45eba135fa22e6554a2bd07555.png"></p> <p style="text-align:center">error_method.png</p> <p>效果图:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/9cc04718e40550a8feea1f20ed28bf75.png"></p> <p style="text-align:center">error_tip.png</p> <p>Empty View 的显示完全一样,就不再讲了。</p> <h3>最后</h3> <p>以上就是对RecyclerView Adapter 的封装和 该库的使用介绍,使用起来非常方便,添加一个列表不再是重复的写Adapter,ViewHolder 等等。添加一个Cell 填充到Adapter 就OK。增加一种Item或者加少一种Item对列表完全没有影响,耦合度几乎为0。</p> <p> </p> <p>来自:http://www.jianshu.com/p/727c18f4bf20</p> <p> </p>