项目中,我们用得最多的元素就是列表了,在Android 中,实现列表用原生的RecyclerView就能满足需求,关于RecyclerView 的基础使用这里不做过多的介绍,网上有太多的博文介绍了。本篇文章将介绍自己封装的一个Adapter,帮你快速高效的添加一个列表(包括单 Item 列表和多item列表)。
理念
1, 构造一个通用的Adapter模版,避免每添加一个列表就要写一个Adapter,避免写Adapter中的大量重复代码。
2,通过组装的方式来构建Adapter,将每一种(ViewType不同的)Item抽象成一个单独组件,Adapter 就是一个壳,我们只需要向Adapter中添加Item就行,这样做的好处就是减少耦合,去掉一种item 或者添加一种item对于列表是没有任何影响的。
3,高内聚,低耦合,扩展方便。
思路
为每一种 viewType 定义一个Cell,Cell就是上面提到的独立组件,它负责创建ViewHolder,数据绑定和逻辑处理。它有2个重要的方法,onCreateViewHolder 负责创建ViewHolder,onBindViewHolder负责数据绑定,这两个方法的定义和生命周期同Adapter种的2个方法一样,事实上,Adapter 中的onCreateViewHolder和onBindViewHolder 最终调用的是Cell中的方法。
** 一种 ViewType 对应一个Cell**
看一个示例:
如上图:以豆瓣APP的首页为例,文章包含图片和视频的两个Item 的布局是不同的,因此,可以添加两个Cell(ImageCell和VideoCell)来分别处理这两种Item。
** 有了Cell之后,要向列表添加添加Header和Footer 的需求就很简单了,我们直接添加一个HeaderCell和FooterCell 就可以了,也不用更改Adapter代码,是不是很方便。此外,还可以用Cell实现列表LoadMore(加载更多)状态、Loadding(加载中)状态、Empty(空页面)状态、Error(出错)状态 View的显示。**
包结构
介绍:1,base:base包下面为Lib的主要代码,一个Cell接口和三个抽象类,分别抽取了Adapter,ViewHolder,Cell的公共逻辑。
2,cell:cell包下面有4个cell,分别显示列表的LoadMore,Loading,Empty,Error状态。
3,fragment:有一个Fragment抽象类,定义了一个UI模版(不需要额外添加布局文件),要实现列表的界面只需要继承AbsBaseFragment,实现几个方法添加数据就OK。
具体代码
1,Cell 接口定义
/**
* 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);
}
定义了4个方法,除了回收资源的方法releaseResource(),其它三个和Adapter中的一样。
2,RVBaseCell
/**
* 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
// 如果有需要回收的资源,子类自己实现
}
}
抽象类,接受一个范型T(Cell接受的数据实体),实现了releaseResource方法,但什么事也没干,因为有很多简单的Cell没有资源回收,就不需要实现。如果子类Cell 有资源回收,重写这个方法就可以了。
3, RVBaseViewHolder
/**
* 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);
}
}
以前写Adapter的时候,每一种viewType 都对应了一个ViewHolder,其中有大量的findViewById绑定视图,有了RVBaseViewHolder,再也不需要定义ViewHolder了,通过id获取View就行,View用SparseArray 保存进行了复用,避免每一次都find。
4,RVBaseAdapter
/**
* 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);
}
RVBaseAdapter 继承 RecyclerView.Adapter,接受的是RVBaseCell类型,保存了一个Cell 列表。其中还有有添加、移除,清空、更新数据的方法。
注意其中几个方法:
1,getItemViewType: 调用的是对应position Cell 的getItemViewType 方法。
2,onCreateViewHolder:调用Cell 的onCreateViewHolder 创建ViewHolder。
3,onBindViewHolder: 调用对应Cell的onBindViewHolder 方法绑定数据
4,onViewDetachedFromWindow: 资源回收
5,RVSimpleAdapter
/**
* 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);
}
}
}
RVSimpleAdapter 是RVBaseAdapter 的默认实现类,添加了显示LoadMore View、Loading View 、Empty View、ErrorView 的功能。
6,AbsBaseFragment
/**
* 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);
}
AbsBaseFragment,实现了上拉加载和下拉刷新功能,添加Toolbar等,上拉加载可以自定义View,下拉刷新用的是Google的SwipeRefreshLayout。要添加一个列表界面,只需要继承AbsBaseFragment,实现几个抽象方法添加Cell就行了,非常方便。
用法示例:
添加一个多Item的列表:
1,创建一个Fragment继承AbsBaseFragment,实现几个方法。
/**
* 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;
}
}
实现上面几个抽象方法,实际上只实现onRecyclerViewInitialized和getCells两个方法就可以实现列表,其它两个方法是下拉刷新和上拉加载的。
2,创建Cell类
/**
* 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);
}
}
}
/**
* 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));
}
}
/**
* 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);
}
}
上面创建了3个Cell,也就是这个列表包含了3种不同类型的Item。
注意:一个列表内,每个Cell 的TYPE要不相同,也就是getItemType方法的返回值要不同。
3,onRecyclerViewInitialized ,做初始化和加载数据
@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);
}
4,实现getCells方法,生成Cell
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;
}
上面根据实体生成不同的Cell。有三种Cell,BannerCell,ImageCell和TextCell。
以上4个步骤就能实现一个界面复杂包含多做Item的列表了效果图如下:
HomePageFragment 的完整代码如下:
/**
* 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);
}
}
Grid 列表和瀑布流列表:
上面演示了添加多Item type 的列表,添加单Item的列表也是一样的,只不过只有一个Cell而已。添加Grid 列表和瀑布流列表差不多的,只是RecylerView 的LayoutManager不同而已。
瀑布流列表示例:
/**
* 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;
}
}
只需要重写initLayoutManager这个方法,返回一个瀑布流的LayoutMannger就可以了。
效果如下:
其它演示示例:LoadMore View 、Loading View 、Error View ,Empty View
1,显示LoadMore View
提供了默认的Loading View,调用代码如下:
mBaseAdapter.showLoadMore();
如果不想用默认的LoadMore View,当然也可以自定义LoadMore View,Adapter 提供方法:
mBaseAdapter.showLoadMore(loadMoreView);
像上面这样提供一个LoadMore View 的布局,还有一个重载的方法,可以指定显示的高度:
mBaseAdapter.showLoadMore(loadMoreView,100);
如果是继承的AbsBaseFragment 创建列表,实现customLoadMoreView方法就ok了:
@Override
protected View customLoadMoreView() {
View loadMoreView = LayoutInflater.from(getContext()).inflate(R.layout.custeom_load_more_layout,null);
return loadMoreView;
}
隐藏LoadMore View 调用如下代码:
if(mBaseAdapter!=null){
mBaseAdapter.hideLoadMore();
}
效果图看上面演示的瀑布流效果图。
**2,显示loading View **
提供了默认的Loading View,调用代码如下:
mBaseAdapter.showLoading();
当然也可以自定义Loading View,提供一个布局即可:
View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null);
mBaseAdapter.showLoading(loadingView);
效果如下:
还有一种情况是,顶部有一个固定的HeaderCell,不需要加载数据,显示静态页面,下面加载数据时需要Loading态,Error状态,Empty状态等等。提供如下3个方法:
showLoadingKeepCount(int keepCount,int height,View loadingView)
列表Loading状态显示的View,保留keepCountg个Item,并指定高度,指定显示的ViewshowLoadingKeepCount(int keepCount,int height)
列表Loading状态显示的View,保留keepCountg个Item,并指定高度(显示的是提供的默认Loading View)showLoadingKeepCount(int keepCount)
显示默认LoadingView
使用代码如下:
View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null);
mBaseAdapter.showLoadingKeepCount(1,height,loadingView);
效果图如下:
隐藏Loading View 调用对应hide 方法:
mBaseAdapter.hideLoading();
** 3, Error View 和 Empty View **
显示Error View 和Empty View 与Loading View 的显示与隐藏是一样,不在过多讲,直接去看源码,提供了几个方法:
效果图:
Empty View 的显示完全一样,就不再讲了。
最后
以上就是对RecyclerView Adapter 的封装和 该库的使用介绍,使用起来非常方便,添加一个列表不再是重复的写Adapter,ViewHolder 等等。添加一个Cell 填充到Adapter 就OK。增加一种Item或者加少一种Item对列表完全没有影响,耦合度几乎为0。更详细的思路分析请看文章RecyclerView 之Adapter的简化过程浅析详细的源码请看Gihub:
Adapter优雅封装-CustomAdapter,欢迎star和follow。