XRecyclerView是對(duì)RecyclerView的封裝,主要完成了下拉刷新、上拉加載更多、添加RecyclerView頭部、顯示空數(shù)據(jù)頁面的功能,項(xiàng)目地址是Github---XRecyclerView。基本的使用,文檔里面有,就不多說了,這里主要記錄的是它怎么實(shí)現(xiàn)的。
XRecyclerView的類圖
1、關(guān)于WrapAdapter
1、創(chuàng)建ViewHolder(onCreateViewHolder())
為了顯示不同類型的Item(HeaderViews、RefreshHeader、FootView),對(duì)傳入的adapter做了封裝:如果itemType是TYPE_REFRESH_HEADER、TYPE_FOOTER、HeaderType,創(chuàng)建SimpleViewHolder類型的ViewHolder進(jìn)行展示,否則調(diào)用adapter.onCreateViewHolder()方法,交給子類創(chuàng)建。
2、綁定數(shù)據(jù)(onBindViewHolder())
需要排除HeaderViews、RefreshHeader、FootView的情況,調(diào)整position,調(diào)用adapter.onBindViewHolder()方法,讓子類綁定。
3、計(jì)算itemType、itemCount等等
大概如下
private WrapAdapter mWrapAdapter;
@Override
public void setAdapter(Adapter adapter) {
//對(duì)傳入的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)) {
//根據(jù)viewtype從對(duì)應(yīng)的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 對(duì)外提供addHeaderView()方法添加header,其中使用了兩個(gè)ArrayList存儲(chǔ)不同的view和viewType。此時(shí),通知mWrapAdapter,刷新界面。
//HEADER_INIT_INDEX是保留值(ReservedItemViewType),如果用戶的adapter與它們重復(fù)將會(huì)強(qiáng)制拋出異常。
private static final int HEADER_INIT_INDEX = 10002;
private ArrayList<View> mHeaderViews = new ArrayList<>();
//每個(gè)header必須有不同的type,不然滾動(dòng)的時(shí)候順序會(huì)變化
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、空白數(shù)據(jù)處理
主要通過AdapterDataObserver 監(jiān)聽數(shù)據(jù)變化,并以此來更換布局(控制EmptyView、XRecyclerView的顯示和隱藏),EmptyView是外部傳進(jìn)來的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、下拉刷新
下拉刷新分為兩部分:手指滑動(dòng),refreshHeader慢慢顯示出來(而且顯示的大小跟滑動(dòng)距離有關(guān));釋放后刷新界面、再慢慢隱藏。需要重寫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) {
//手指滑動(dòng)距離的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刷新和慢慢向上隱藏的動(dòng)作
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(),因?yàn)閂iewHolder是被復(fù)用的,一屏里最多有N+2個(gè)ViewHolder,如果recycleVIew沒有在頂部,它的ViewHolder可能被其他的item復(fù)用了,所以mRefreshHeader.getParent()==null
5、上拉加載更多
不喜勿噴