可上拉加载和下拉刷新的XRecyclerView
来源:互联网 发布:linux oracle 客户端 编辑:程序博客网 时间:2024/06/05 22:33
public class XRecyclerView extends RecyclerView { private boolean isLoadingData = false; private boolean isNoMore = false; private int mRefreshProgressStyle = ProgressStyle.SysProgress; private int mLoadingMoreProgressStyle = ProgressStyle.SysProgress; private ArrayList<View> mHeaderViews = new ArrayList<>(); private WrapAdapter mWrapAdapter; private float mLastY = -1; private static final float DRAG_RATE = 3; private LoadingListener mLoadingListener; private ArrowRefreshHeader mRefreshHeader; private boolean pullRefreshEnabled = true; private boolean loadingMoreEnabled = true; //下面的ItemViewType是保留值(ReservedItemViewType),如果用户的adapter与它们重复将会强制抛出异常。不过为了简化,我们检测到重复时对用户的提示是ItemViewType必须小于10000 private static final int TYPE_REFRESH_HEADER = 10000;//设置一个很大的数字,尽可能避免和用户的adapter冲突 private static final int TYPE_FOOTER = 10001; private static final int HEADER_INIT_INDEX = 10002; private static List<Integer> sHeaderTypes = new ArrayList<>();//每个header必须有不同的type,不然滚动的时候顺序会变化 private int mPageCount = 0; //adapter没有数据的时候显示,类似于listView的emptyView private View mEmptyView; private View mFootView; private final RecyclerView.AdapterDataObserver mDataObserver = new DataObserver(); private AppBarStateChangeListener.State appbarState = AppBarStateChangeListener.State.EXPANDED; public XRecyclerView(Context context) { this(context, null); } public XRecyclerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public XRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { if (pullRefreshEnabled) { mRefreshHeader = new ArrowRefreshHeader(getContext()); mRefreshHeader.setProgressStyle(mRefreshProgressStyle); } LoadingMoreFooter footView = new LoadingMoreFooter(getContext()); footView.setProgressStyle(mLoadingMoreProgressStyle); mFootView = footView; mFootView.setVisibility(GONE); } public void addHeaderView(View view) { sHeaderTypes.add(HEADER_INIT_INDEX + mHeaderViews.size()); mHeaderViews.add(view); if (mWrapAdapter != null) { mWrapAdapter.notifyDataSetChanged(); } } //根据header的ViewType判断是哪个header private View getHeaderViewByType(int itemType) { if(!isHeaderType(itemType)) { return null; } return mHeaderViews.get(itemType - HEADER_INIT_INDEX); } //判断一个type是否为HeaderType private boolean isHeaderType(int itemViewType) { return mHeaderViews.size() > 0 && sHeaderTypes.contains(itemViewType); } //判断是否是XRecyclerView保留的itemViewType private boolean isReservedItemViewType(int itemViewType) { if(itemViewType == TYPE_REFRESH_HEADER || itemViewType == TYPE_FOOTER || sHeaderTypes.contains(itemViewType)) { return true; } else { return false; } } public void setFootView(final View view) { mFootView = view; } public void loadMoreComplete() { isLoadingData = false; if (mFootView instanceof LoadingMoreFooter) { ((LoadingMoreFooter) mFootView).setState(LoadingMoreFooter.STATE_COMPLETE); } else { mFootView.setVisibility(View.GONE); } } public void setNoMore(boolean noMore){ isLoadingData = false; isNoMore = noMore; if (mFootView instanceof LoadingMoreFooter) { ((LoadingMoreFooter) mFootView).setState(isNoMore ? LoadingMoreFooter.STATE_NOMORE:LoadingMoreFooter.STATE_COMPLETE); } else { mFootView.setVisibility(View.GONE); } } public void refresh() { if (pullRefreshEnabled && mLoadingListener != null) { mRefreshHeader.setState(ArrowRefreshHeader.STATE_REFRESHING); mLoadingListener.onRefresh(); } } public void reset(){ setNoMore(false); loadMoreComplete(); refreshComplete(); } public void refreshComplete() { mRefreshHeader.refreshComplete(); setNoMore(false); } public void setRefreshHeader(ArrowRefreshHeader refreshHeader) { mRefreshHeader = refreshHeader; } public void setPullRefreshEnabled(boolean enabled) { pullRefreshEnabled = enabled; } public void setLoadingMoreEnabled(boolean enabled) { loadingMoreEnabled = enabled; if (!enabled) { if (mFootView instanceof LoadingMoreFooter) { ((LoadingMoreFooter)mFootView).setState(LoadingMoreFooter.STATE_COMPLETE); } } } public void setRefreshProgressStyle(int style) { mRefreshProgressStyle = style; if (mRefreshHeader != null) { mRefreshHeader.setProgressStyle(style); } } public void setLoadingMoreProgressStyle(int style) { mLoadingMoreProgressStyle = style; if (mFootView instanceof LoadingMoreFooter) { ((LoadingMoreFooter) mFootView).setProgressStyle(style); } } public void setArrowImageView(int resId) { if (mRefreshHeader != null) { mRefreshHeader.setArrowImageView(resId); } } public void setEmptyView(View emptyView) { this.mEmptyView = emptyView; mDataObserver.onChanged(); } public View getEmptyView() { return mEmptyView; } @Override public void setAdapter(Adapter adapter) { mWrapAdapter = new WrapAdapter(adapter); super.setAdapter(mWrapAdapter); adapter.registerAdapterDataObserver(mDataObserver); mDataObserver.onChanged(); } //避免用户自己调用getAdapter() 引起的ClassCastException @Override public Adapter getAdapter() { if(mWrapAdapter != null) return mWrapAdapter.getOriginalAdapter(); else return null; } @Override public void setLayoutManager(LayoutManager layout) { super.setLayoutManager(layout); if(mWrapAdapter != null){ if (layout instanceof GridLayoutManager) { final GridLayoutManager gridManager = ((GridLayoutManager) layout); gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return (mWrapAdapter.isHeader(position) || mWrapAdapter.isFooter(position) || mWrapAdapter.isRefreshHeader(position)) ? gridManager.getSpanCount() : 1; } }); } } } @Override public void onScrollStateChanged(int state) { super.onScrollStateChanged(state); if (state == RecyclerView.SCROLL_STATE_IDLE && mLoadingListener != null && !isLoadingData && loadingMoreEnabled) { LayoutManager layoutManager = getLayoutManager(); int lastVisibleItemPosition; if (layoutManager instanceof GridLayoutManager) { lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into); lastVisibleItemPosition = findMax(into); } else { lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); } if (layoutManager.getChildCount() > 0 && lastVisibleItemPosition >= layoutManager.getItemCount() - 1 && layoutManager.getItemCount() > layoutManager.getChildCount() && !isNoMore && mRefreshHeader.getState() < ArrowRefreshHeader.STATE_REFRESHING) { isLoadingData = true; if (mFootView instanceof LoadingMoreFooter) { ((LoadingMoreFooter) mFootView).setState(LoadingMoreFooter.STATE_LOADING); } else { mFootView.setVisibility(View.VISIBLE); } mLoadingListener.onLoadMore(); } } } @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 && appbarState == AppBarStateChangeListener.State.EXPANDED) { mRefreshHeader.onMove(deltaY / DRAG_RATE); if (mRefreshHeader.getVisibleHeight() > 0 && mRefreshHeader.getState() < ArrowRefreshHeader.STATE_REFRESHING) { return false; } } break; default: mLastY = -1; // reset if (isOnTop() && pullRefreshEnabled && appbarState == AppBarStateChangeListener.State.EXPANDED) { if (mRefreshHeader.releaseAction()) { if (mLoadingListener != null) { mLoadingListener.onRefresh(); } } } break; } return super.onTouchEvent(ev); } private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value > max) { max = value; } } return max; } private boolean isOnTop() { if (mRefreshHeader.getParent() != null) { return true; } else { return false; } } 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); } } } @Override public void onItemRangeInserted(int positionStart, int itemCount) { mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount); } @Override public void onItemRangeChanged(int positionStart, int itemCount) { mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount); } @Override public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount); } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { mWrapAdapter.notifyItemMoved(fromPosition, toPosition); } }; private class WrapAdapter extends RecyclerView.Adapter<ViewHolder> { private RecyclerView.Adapter adapter; public WrapAdapter(RecyclerView.Adapter adapter) { this.adapter = adapter; } public RecyclerView.Adapter getOriginalAdapter(){ return this.adapter; } public boolean isHeader(int position) { return position >= 1 && position < mHeaderViews.size() + 1; } public boolean isFooter(int position) { if(loadingMoreEnabled) { return position == getItemCount() - 1; }else { return false; } } public boolean isRefreshHeader(int position) { return position == 0; } public int getHeadersCount() { return mHeaderViews.size(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_REFRESH_HEADER) { return new SimpleViewHolder(mRefreshHeader); } else if (isHeaderType(viewType)) { 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); } } } // some times we need to override this @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position,List<Object> payloads) { if (isHeader(position) || isRefreshHeader(position)) { return; } int adjPosition = position - (getHeadersCount() + 1); int adapterCount; if (adapter != null) { adapterCount = adapter.getItemCount(); if (adjPosition < adapterCount) { if(payloads.isEmpty()){ adapter.onBindViewHolder(holder, adjPosition); } else{ adapter.onBindViewHolder(holder, adjPosition,payloads); } } } } @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; } @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if (manager instanceof GridLayoutManager) { final GridLayoutManager gridManager = ((GridLayoutManager) manager); gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return (isHeader(position) || isFooter(position) || isRefreshHeader(position)) ? gridManager.getSpanCount() : 1; } }); } adapter.onAttachedToRecyclerView(recyclerView); } @Override public void onDetachedFromRecyclerView(RecyclerView recyclerView) { adapter.onDetachedFromRecyclerView(recyclerView); } @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { super.onViewAttachedToWindow(holder); ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams && (isHeader(holder.getLayoutPosition()) ||isRefreshHeader(holder.getLayoutPosition()) || isFooter(holder.getLayoutPosition()))) { StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp; p.setFullSpan(true); } adapter.onViewAttachedToWindow(holder); } @Override public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { adapter.onViewDetachedFromWindow(holder); } @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { adapter.onViewRecycled(holder); } @Override public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) { return adapter.onFailedToRecycleView(holder); } @Override public void unregisterAdapterDataObserver(AdapterDataObserver observer) { adapter.unregisterAdapterDataObserver(observer); } @Override public void registerAdapterDataObserver(AdapterDataObserver observer) { adapter.registerAdapterDataObserver(observer); } private class SimpleViewHolder extends RecyclerView.ViewHolder { public SimpleViewHolder(View itemView) { super(itemView); } } } public void setLoadingListener(LoadingListener listener) { mLoadingListener = listener; } public interface LoadingListener { void onRefresh(); void onLoadMore(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); //解决和CollapsingToolbarLayout冲突的问题 AppBarLayout appBarLayout = null; ViewParent p = getParent(); while (p != null) { if (p instanceof CoordinatorLayout) { break; } p = p.getParent(); } if(p instanceof CoordinatorLayout) { CoordinatorLayout coordinatorLayout = (CoordinatorLayout)p; final int childCount = coordinatorLayout.getChildCount(); for (int i = childCount - 1; i >= 0; i--) { final View child = coordinatorLayout.getChildAt(i); if(child instanceof AppBarLayout) { appBarLayout = (AppBarLayout)child; break; } } if(appBarLayout != null) { appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() { @Override public void onStateChanged(AppBarLayout appBarLayout, State state) { appbarState = state; } }); } } } public class DividerItemDecoration extends RecyclerView.ItemDecoration { private Drawable mDivider; private int mOrientation; /** * Sole constructor. Takes in a {@link Drawable} to be used as the interior * divider. * * @param divider A divider {@code Drawable} to be drawn on the RecyclerView */ public DividerItemDecoration(Drawable divider) { mDivider = divider; } /** * Draws horizontal or vertical dividers onto the parent RecyclerView. * * @param canvas The {@link Canvas} onto which dividers will be drawn * @param parent The RecyclerView onto which dividers are being added * @param state The current RecyclerView.State of the RecyclerView */ @Override public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) { if (mOrientation == LinearLayoutManager.HORIZONTAL) { drawHorizontalDividers(canvas, parent); } else if (mOrientation == LinearLayoutManager.VERTICAL) { drawVerticalDividers(canvas, parent); } } /** * Determines the size and location of offsets between items in the parent * RecyclerView. * * @param outRect The {@link Rect} of offsets to be added around the child * view * @param view The child view to be decorated with an offset * @param parent The RecyclerView onto which dividers are being added * @param state The current RecyclerView.State of the RecyclerView */ @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); if (parent.getChildAdapterPosition(view) <= mWrapAdapter.getHeadersCount() + 1) { return; } mOrientation = ((LinearLayoutManager) parent.getLayoutManager()).getOrientation(); if (mOrientation == LinearLayoutManager.HORIZONTAL) { outRect.left = mDivider.getIntrinsicWidth(); } else if (mOrientation == LinearLayoutManager.VERTICAL) { outRect.top = mDivider.getIntrinsicHeight(); } } /** * Adds dividers to a RecyclerView with a LinearLayoutManager or its * subclass oriented horizontally. * * @param canvas The {@link Canvas} onto which horizontal dividers will be * drawn * @param parent The RecyclerView onto which horizontal dividers are being * added */ private void drawHorizontalDividers(Canvas canvas, RecyclerView parent) { int parentTop = parent.getPaddingTop(); int parentBottom = parent.getHeight() - parent.getPaddingBottom(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount - 1; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); int parentLeft = child.getRight() + params.rightMargin; int parentRight = parentLeft + mDivider.getIntrinsicWidth(); mDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom); mDivider.draw(canvas); } } /** * Adds dividers to a RecyclerView with a LinearLayoutManager or its * subclass oriented vertically. * * @param canvas The {@link Canvas} onto which vertical dividers will be * drawn * @param parent The RecyclerView onto which vertical dividers are being * added */ private void drawVerticalDividers(Canvas canvas, RecyclerView parent) { int parentLeft = parent.getPaddingLeft(); int parentRight = parent.getWidth() - parent.getPaddingRight(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount - 1; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); int parentTop = child.getBottom() + params.bottomMargin; int parentBottom = parentTop + mDivider.getIntrinsicHeight(); mDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom); mDivider.draw(canvas); } } }}
布局
<com.jcodecraeer.xrecyclerview.XRecyclerView android:id="@+id/xrv_list" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scrollbarSize="0.5dp" android:scrollbars="vertical" />
阅读全文
0 0
- 可上拉加载和下拉刷新的XRecyclerView
- XRecyclerView的下拉刷新和上拉加载
- XRecyclerView下拉刷新和上拉加载
- XRecyclerView的刷新和加载
- xrecyclerview的刷新和加载
- XRecyclerView的上拉加载,下拉刷新
- RecyclerView的刷新和加载--XRecyclerView
- XRecyclerView 实现下拉刷新和上拉加载更多
- xRecyclerView简单实现下拉刷新和上拉加载
- xRecyclerView简单实现下拉刷新和上拉加载
- xrecyclerview 实现下拉刷新下拉加载
- MVP和sqlite数据库,XRecyclerView上啦加载下拉刷新的第三方注册登录,列表
- 支持下拉刷新上拉加载xrecyclerview的基本使用
- Xrecyclerview的使用(上拉刷新,下拉加载)
- XRecyclerView的下拉刷新上拉加载功能
- RecyclerView的上拉加载下拉刷新-xRecyclerView
- XRecyclerView的使用下拉刷新
- SwipeRefreshLayout下拉刷新,加载更多XRecyclerView
- java 发送邮件和读取邮件(163邮箱为例)
- robots.txt文件的作用
- Android.mk 中解决Warning:warning: shared library text segment is not shareable
- 用Java数字签名提供XML安全
- 继承bean配置,依赖bean配置
- 可上拉加载和下拉刷新的XRecyclerView
- 根据中华人民共和国身份证生成规则,校验身份证
- Python 字典遍历
- 运算符
- java一维数组打印杨辉三角
- 欢迎使用CSDN-markdown编辑器
- HDOJ 1874 畅通工程续 (dijkstra算法)
- LeetCode
- 安装Web Storm