XRecyclerView是对RecyclerView的封装,主要完成了下拉刷新、上拉加载更多、添加RecyclerView头部、显示空数据页面的功能,项目地址是Github---XRecyclerView。基本的使用,文档里面有,就不多说了,这里主要记录的是它怎么实现的。
XRecyclerView的类图
1、关于WrapAdapter
1、创建ViewHolder(onCreateViewHolder())
为了显示不同类型的Item(HeaderViews、RefreshHeader、FootView),对传入的adapter做了封装:如果itemType是TYPE_REFRESH_HEADER、TYPE_FOOTER、HeaderType,创建SimpleViewHolder类型的ViewHolder进行展示,否则调用adapter.onCreateViewHolder()方法,交给子类创建。
2、绑定数据(onBindViewHolder())
需要排除HeaderViews、RefreshHeader、FootView的情况,调整position,调用adapter.onBindViewHolder()方法,让子类绑定。
3、计算itemType、itemCount等等
大概如下
private WrapAdapter mWrapAdapter;
@Override
public void setAdapter(Adapter adapter) {
//对传入的adapter做封装
mWrapAdapter = new WrapAdapter(adapter);
super.setAdapter(mWrapAdapter);
adapter.registerAdapterDataObserver(mDataObserver);
mDataObserver.onChanged();
}
private class WrapAdapter extends RecyclerView.Adapter<ViewHolder> {
private RecyclerView.Adapter adapter;
public WrapAdapter(RecyclerView.Adapter adapter) {
this.adapter = adapter;
}
...
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_REFRESH_HEADER) {
return new SimpleViewHolder(mRefreshHeader);
} else if (isHeaderType(viewType)) {
//根据viewtype从对应的ArrayList中取出view来展示
return new SimpleViewHolder(getHeaderViewByType(viewType));
} else if (viewType == TYPE_FOOTER) {
return new SimpleViewHolder(mFootView);
}
return adapter.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeader(position) || isRefreshHeader(position)) {
return;
}
int adjPosition = position - (getHeadersCount() + 1);
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
adapter.onBindViewHolder(holder, adjPosition);
}
}
}
@Override
public int getItemCount() {
if(loadingMoreEnabled) {
if (adapter != null) {
return getHeadersCount() + adapter.getItemCount() + 2;
} else {
return getHeadersCount() + 2;
}
}else {
if (adapter != null) {
return getHeadersCount() + adapter.getItemCount() + 1;
} else {
return getHeadersCount() + 1;
}
}
}
@Override
public int getItemViewType(int position) {
int adjPosition = position - (getHeadersCount() + 1);
if (isRefreshHeader(position)) {
return TYPE_REFRESH_HEADER;
}
if (isHeader(position)) {
position = position - 1;
return sHeaderTypes.get(position);
}
if (isFooter(position)) {
return TYPE_FOOTER;
}
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
int type = adapter.getItemViewType(adjPosition);
if(isReservedItemViewType(type)) {
throw new IllegalStateException("XRecyclerView require itemViewType in adapter should be less than 10000 " );
}
return type;
}
}
return 0;
}
@Override
public long getItemId(int position) {
if (adapter != null && position >= getHeadersCount() + 1) {
int adjPosition = position - (getHeadersCount() + 1);
if (adjPosition < adapter.getItemCount()) {
return adapter.getItemId(adjPosition);
}
}
return -1;
}
private class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
...
}
2、添加HeaderViews
XRecyclerView 对外提供addHeaderView()方法添加header,其中使用了两个ArrayList存储不同的view和viewType。此时,通知mWrapAdapter,刷新界面。
//HEADER_INIT_INDEX是保留值(ReservedItemViewType),如果用户的adapter与它们重复将会强制抛出异常。
private static final int HEADER_INIT_INDEX = 10002;
private ArrayList<View> mHeaderViews = new ArrayList<>();
//每个header必须有不同的type,不然滚动的时候顺序会变化
private static List<Integer> sHeaderTypes = new ArrayList<>();
public void addHeaderView(View view) {
sHeaderTypes.add(HEADER_INIT_INDEX + mHeaderViews.size());
mHeaderViews.add(view);
if (mWrapAdapter != null) {
//刷新界面
mWrapAdapter.notifyDataSetChanged();
}
}
3、空白数据处理
主要通过AdapterDataObserver 监听数据变化,并以此来更换布局(控制EmptyView、XRecyclerView的显示和隐藏),EmptyView是外部传进来的View。
public void setEmptyView(View emptyView) {
this.mEmptyView = emptyView;
mDataObserver.onChanged();
}
private class DataObserver extends RecyclerView.AdapterDataObserver {
@Override
public void onChanged() {
if (mWrapAdapter != null) {
mWrapAdapter.notifyDataSetChanged();
}
if (mWrapAdapter != null && mEmptyView != null) {
int emptyCount = 1 + mWrapAdapter.getHeadersCount();
if (loadingMoreEnabled) {
emptyCount++;
}
if (mWrapAdapter.getItemCount() == emptyCount) {
mEmptyView.setVisibility(View.VISIBLE);
XRecyclerView.this.setVisibility(View.GONE);
} else {
mEmptyView.setVisibility(View.GONE);
XRecyclerView.this.setVisibility(View.VISIBLE);
}
}
}
...
}
4、下拉刷新
下拉刷新分为两部分:手指滑动,refreshHeader慢慢显示出来(而且显示的大小跟滑动距离有关);释放后刷新界面、再慢慢隐藏。需要重写onTouchEvent,主要代码如下
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) {
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getRawY() - mLastY;
mLastY = ev.getRawY();
if (isOnTop() && pullRefreshEnabled) {
//手指滑动距离的1/3,作为RefreshHeader显示的高度
mRefreshHeader.onMove(deltaY / DRAG_RATE);
if (mRefreshHeader.getVisibleHeight() > 0 && mRefreshHeader.getState() < IRefreshHeader.STATE_REFRESHING) {
return false;
}
}
break;
default:
mLastY = -1; // reset
if (isOnTop() && pullRefreshEnabled) {
//在releaseAction()中处理了手指释放后,RefreshHeader刷新和慢慢向上隐藏的动作
if (mRefreshHeader.releaseAction()) {
if (mLoadingListener != null) {
mLoadingListener.onRefresh();
}
}
}
break;
}
return super.onTouchEvent(ev);
}
private boolean isOnTop() {
if (mRefreshHeader.getParent() != null) {
return true;
} else {
return false;
}
}
//RefreshHeader慢慢向上隐藏
public void refreshComplete() {
mRefreshHeader.refreshComplete();
}
其中,判断是否在顶部,使用的是mRefreshHeader.getParent(),因为ViewHolder是被复用的,一屏里最多有N+2个ViewHolder,如果recycleVIew没有在顶部,它的ViewHolder可能被其他的item复用了,所以mRefreshHeader.getParent()==null
5、上拉加载更多
不喜勿喷