RecyclerView实现下拉刷新和上拉加载更多
2016-03-07 15:04
441 查看
为了优化用户体验,从App的性能和节省用户流量方面考虑,都会涉及到对列表数据进行分页加载,使用ListView时大多都是通过自定义ListView增加头部和底部来实现下拉刷新和上拉加载更多功能,使用RecyclerView也需要处理一些滑动事件才能达到分页的效果,下面是看了一些网上的开源代码后,自己实现的一个可下拉刷新和上拉加载的RecyclerView;
ARecycleView类的布局文件:
调用代码:
RecyclerView的下拉刷新是android.support.v4.widget.SwipeRefreshLayout控制实现的,滑动到底部自动加载下页内容主要在ARecyclerView类的这部分代码实现的:
解释下这段代码,判断条件流程:
1.加载更多的监听器是否为空,是否正在执行加载更多操作;
2.是否允许加载更多canShowLoadmore ,这个是在setTotal(int mTotal)方法里设置的,服务器返回的接口数据里,分页时一般都会返回total字段,调用ARecyclerView的setTotal方法设置进去后,加载完数据后会自动禁止掉加载更多的操作;
3.滑动状态是否为SCROLL_STATE_IDLE(停止滑动状态);
4.页面上显示出来的最后一个item的position是否等于当前适配器的总item数;
5.是否设置了数据总数量mTotal,页面上显示出来的最后一个item的position是否小于mTotal;
如果满足上面的所有条件,调用loadMore() 方法。
OK,上面这些就是实现RecyclerView的主要部分代码,建议有时间都自己自定义下RecyclerView这个类,可以增进自己对这个控件的了解,使用时更灵活,需要什么方法可以自己琢磨下往里加。
源码
package com.lcp.arecyclerview.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.util.AttributeSet; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewStub; import android.widget.FrameLayout; import android.widget.RelativeLayout; import com.lcp.arecyclerview.R; import java.util.Arrays; /** * Created by Aislli on 2015/12/29. */ public class ARecycleView extends FrameLayout { private int mPadding; private int mPaddingTop; private int mPaddingBottom; private int mPaddingLeft; private int mPaddingRight; private boolean mClipToPadding; private int mEmptyId; private RecyclerView mRecyclerView; private SwipeRefreshLayout mSwipeRefreshLayout; private ViewStub mEmpty; private View mEmptyView; private ARecyclerBaseAdapter mAdapter; private boolean isLoadingMore; private OnLoadMoreListener onLoadMoreListener; private RecyclerView.LayoutManager mLayoutManager; /** * 数据总条数 */ private int mTotal; private int mMoreProgressId; private ViewStub mMoreProgress; private View mMoreProgressView; /** * 是否可以显示加载更多 */ private boolean canShowLoadmore; int itemCount = 0; public ARecycleView(Context context) { super(context); initViews(); } public ARecycleView(Context context, AttributeSet attrs) { super(context, attrs); initAttrs(attrs); initViews(); } public ARecycleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(attrs); initViews(); } protected void initAttrs(AttributeSet attrs) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.AislliRecyclerview); try { mPadding = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_Padding, -1.1f); mPaddingTop = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingTop, 0.0f); mPaddingBottom = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingBottom, 0.0f); mPaddingLeft = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingLeft, 0.0f); mPaddingRight = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingRight, 0.0f); mClipToPadding = typedArray.getBoolean(R.styleable.AislliRecyclerview_ClipToPadding, false); mEmptyId = typedArray.getResourceId(R.styleable.AislliRecyclerview_EmptyView, 0); mMoreProgressId = typedArray.getResourceId(R.styleable.AislliRecyclerview_layout_moreProgress, R.layout.view_more_progress); } finally { typedArray.recycle(); } } protected void initViews() { LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.layout_recyclerview, this); mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_list); mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swiperefresh_layout); mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light); mSwipeRefreshLayout.setEnabled(false); mSwipeRefreshLayout.setProgressViewOffset(false, 0, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources() .getDisplayMetrics())); if (mRecyclerView != null) { mRecyclerView.setClipToPadding(mClipToPadding); if (mPadding > 0) { mRecyclerView.setPadding(mPadding, mPadding, mPadding, mPadding); } else { mRecyclerView.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom); } } setScrollListener(); mMoreProgress = (ViewStub) view.findViewById(R.id.more_progress); mMoreProgress.setLayoutResource(mMoreProgressId); if (mMoreProgressId != 0) mMoreProgressView = mMoreProgress.inflate(); mMoreProgress.setVisibility(View.GONE); mEmpty = (ViewStub) view.findViewById(R.id.emptyview); mEmpty.setLayoutResource(mEmptyId); if (mEmptyId != 0) mEmptyView = mEmpty.inflate(); mEmpty.setVisibility(View.GONE); } private void setScrollListener() { mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (null == onLoadMoreListener || isLoadingMore) { return; } int currentCount = 0; if (mLayoutManager instanceof LinearLayoutManager) { currentCount = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition() + 1; } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null); Arrays.sort(lastVisibleItemPositions); currentCount = lastVisibleItemPositions[lastVisibleItemPositions.length - 1] + 1; } itemCount = mAdapter.getItemCount(); if (canShowLoadmore && newState == RecyclerView.SCROLL_STATE_IDLE && currentCount == itemCount) { if (mTotal > 0 && currentCount < mTotal) { isLoadingMore = true; mMoreProgressView.setVisibility(VISIBLE); onLoadMoreListener.loadMore(); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); } }); } public void setLayoutManager(RecyclerView.LayoutManager manager) { mLayoutManager = manager; mRecyclerView.setLayoutManager(mLayoutManager); } public void setAdapter(ARecyclerBaseAdapter adapter) { mAdapter = adapter; mRecyclerView.setAdapter(mAdapter); if (mSwipeRefreshLayout != null) mSwipeRefreshLayout.setRefreshing(false); if (mAdapter != null) mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onItemRangeChanged(int positionStart, int itemCount) { super.onItemRangeChanged(positionStart, itemCount); updateHelperDisplays(); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { super.onItemRangeInserted(positionStart, itemCount); updateHelperDisplays(); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { super.onItemRangeRemoved(positionStart, itemCount); updateHelperDisplays(); } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { super.onItemRangeMoved(fromPosition, toPosition, itemCount); updateHelperDisplays(); } @Override public void onChanged() { super.onChanged(); updateHelperDisplays(); } }); if ((adapter == null || mAdapter.getItemCount() == 0) && mEmptyId != 0) { mEmpty.setVisibility(View.VISIBLE); } } private void updateHelperDisplays() { isLoadingMore = false; if (mSwipeRefreshLayout != null) mSwipeRefreshLayout.setRefreshing(false); if (mAdapter == null) return; if (mAdapter.getItemCount() == 0) { mEmpty.setVisibility(mEmptyId != 0 ? View.VISIBLE : View.GONE); } else if (mEmptyId != 0) { mEmpty.setVisibility(View.GONE); } } /** * Sets the {@link RecyclerView.ItemAnimator} that will handle animations involving changes * to the items in this RecyclerView. By default, RecyclerView instantiates and * uses an instance of {@link android.support.v7.widget.DefaultItemAnimator}. Whether item animations are enabled for the RecyclerView depends on the ItemAnimator and whether * the LayoutManager {@link android.support.v7.widget.RecyclerView.LayoutManager#supportsPredictiveItemAnimations() * supports item animations}. * * @param animator The ItemAnimator being set. If null, no animations will occur * when changes occur to the items in this RecyclerView. */ public void setItemAnimator(RecyclerView.ItemAnimator animator) { mRecyclerView.setItemAnimator(animator); } public void setHasFixedSize(boolean hasFixedSize) { mRecyclerView.setHasFixedSize(hasFixedSize); } public void setRefreshing(boolean refreshing) { if (mSwipeRefreshLayout != null) mSwipeRefreshLayout.setRefreshing(refreshing); mLayoutManager.scrollToPosition(0); } public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) { mSwipeRefreshLayout.setEnabled(true); mSwipeRefreshLayout.setOnRefreshListener(listener); } /** * Set the load more listener of recyclerview * * @param onLoadMoreListener load listen */ public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) { this.onLoadMoreListener = onLoadMoreListener; } public void setTotal(int mTotal) { this.mTotal = mTotal; canShowLoadmore = true; } public void showMoreProgress() { mMoreProgress.setVisibility(View.VISIBLE); } public void hideMoreProgress() { mMoreProgress.setVisibility(View.GONE); canShowLoadmore = true; mLayoutManager.scrollToPosition(itemCount); } public void hideLoading() { if (mSwipeRefreshLayout != null) mSwipeRefreshLayout.setRefreshing(false); mMoreProgress.setVisibility(View.GONE); canShowLoadmore = true; } private static boolean isParallaxHeader = false; public static class CustomRelativeWrapper extends RelativeLayout { private int mOffset; public CustomRelativeWrapper(Context context) { super(context); } @Override protected void dispatchDraw(Canvas canvas) { if (isParallaxHeader) canvas.clipRect(new Rect(getLeft(), getTop(), getRight(), getBottom() + mOffset)); super.dispatchDraw(canvas); } public void setClipY(int offset) { mOffset = offset; invalidate(); } } /** * Add an {@link RecyclerView.ItemDecoration} to this RecyclerView. Item decorations can affect both measurement and drawing of individual item views. Item decorations are ordered. Decorations placed earlier in the list will be run/queried/drawn first for their effects on item views. Padding added to views will be nested; a padding added by an earlier decoration will mean further item decorations in the list will be asked to draw/pad within the previous decoration's given area. * * @param itemDecoration Decoration to add */ public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration) { mRecyclerView.addItemDecoration(itemDecoration); } public interface OnLoadMoreListener { void loadMore(); } }
ARecycleView类的布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff"> <ViewStub android:id="@+id/more_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:inflatedId="@id/more_progress"/> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swiperefresh_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/more_progress"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view_list" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="@null" android:scrollbars="none"/> </android.support.v4.widget.SwipeRefreshLayout> <ViewStub android:id="@+id/emptyview" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:layout_marginTop="8dp" android:visibility="gone"/> </RelativeLayout>
调用代码:
package com.lcp.arecyclerview; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.Toast; import com.lcp.arecyclerview.adapter.RcListAdapter; import com.lcp.arecyclerview.widget.ARecycleView; import com.lcp.arecyclerview.widget.ARecyclerBaseAdapter; import com.lcp.arecyclerview.widget.DividerItemDecoration; import java.util.ArrayList; /** * ListView and GridView * Created by Aislli on 2016/2/29. */ public class RecyclerListActivity extends Activity { private ARecycleView mRecyclerView; private ArrayList<String> list; private RcListAdapter mReListAdapter; private RecyclerView.LayoutManager linearLayoutManager; Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.content_list); initView(); initData(); } private void initView() { mRecyclerView = (ARecycleView) findViewById(R.id.recyclerview); } private void initData() { int flag = getIntent().getIntExtra("flag", 0); list = new ArrayList<>(); for (int i = 0; i < 60; i++) { list.add("item:" + i); } if (flag == 0) {//线性布局,ListView效果 linearLayoutManager = new LinearLayoutManager(this); mReListAdapter = new RcListAdapter(this, list); mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.HORIZONTAL)); } else if (flag == 1) {//GridView效果 linearLayoutManager = new GridLayoutManager(this, 3); mReListAdapter = new RcListAdapter(this, list); } mRecyclerView.setLayoutManager(linearLayoutManager); mRecyclerView.setAdapter(mReListAdapter); mRecyclerView.setTotal(list.size() + 6);//设置数据总数,加载完了就禁止掉加载更多事件 setListener(); } int moreNum = 1; int refreshNum = 1; private void setListener() { //下拉刷新 mRecyclerView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { handler.postDelayed(new Runnable() { @Override public void run() { mReListAdapter.insert(refreshNum++ + " Refresh things", 0); mRecyclerView.setRefreshing(false); } }, 1500); } }); //上拉加载更多 mRecyclerView.setOnLoadMoreListener(new ARecycleView.OnLoadMoreListener() { @Override public void loadMore() { handler.postDelayed(new Runnable() { public void run() { mReListAdapter.insert("More " + moreNum++, mReListAdapter.getItemCount()); mRecyclerView.hideMoreProgress(); } }, 1500); } }); //点击事件 mReListAdapter.setOnItemClickListener(new RcListAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(getApplicationContext(), "position:" + position, Toast.LENGTH_SHORT).show(); } }); //长按点击 mReListAdapter.setOnItemLongClickListener(new ARecyclerBaseAdapter.OnItemLongClickListener() { @Override public void onItemLongClick(View view, int position) { Toast.makeText(getApplicationContext(), "long position:" + position, Toast.LENGTH_SHORT).show(); mReListAdapter.remove(position); } }); } }
RecyclerView的下拉刷新是android.support.v4.widget.SwipeRefreshLayout控制实现的,滑动到底部自动加载下页内容主要在ARecyclerView类的这部分代码实现的:
private void setScrollListener() { mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (null == onLoadMoreListener || isLoadingMore) { return; } int currentCount = 0; //判断最后一个item的position if (mLayoutManager instanceof LinearLayoutManager) { currentCount = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition() + 1; } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null); Arrays.sort(lastVisibleItemPositions); currentCount = lastVisibleItemPositions[lastVisibleItemPositions.length - 1] + 1; } itemCount = mAdapter.getItemCount(); if (canShowLoadmore && newState == RecyclerView.SCROLL_STATE_IDLE && currentCount == itemCount) { if (mTotal > 0 && currentCount < mTotal) { isLoadingMore = true; mMoreProgressView.setVisibility(VISIBLE); onLoadMoreListener.loadMore(); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); } }); }
解释下这段代码,判断条件流程:
1.加载更多的监听器是否为空,是否正在执行加载更多操作;
2.是否允许加载更多canShowLoadmore ,这个是在setTotal(int mTotal)方法里设置的,服务器返回的接口数据里,分页时一般都会返回total字段,调用ARecyclerView的setTotal方法设置进去后,加载完数据后会自动禁止掉加载更多的操作;
3.滑动状态是否为SCROLL_STATE_IDLE(停止滑动状态);
4.页面上显示出来的最后一个item的position是否等于当前适配器的总item数;
5.是否设置了数据总数量mTotal,页面上显示出来的最后一个item的position是否小于mTotal;
如果满足上面的所有条件,调用loadMore() 方法。
OK,上面这些就是实现RecyclerView的主要部分代码,建议有时间都自己自定义下RecyclerView这个类,可以增进自己对这个控件的了解,使用时更灵活,需要什么方法可以自己琢磨下往里加。
源码
相关文章推荐
- 完美实现Android ListView中的TextView的跑马灯效果
- android上改变listView的选中颜色
- more、less 和 most 的区别
- Delphi7中Listview的常用功能汇总
- Delphi控件ListView的属性及使用方法详解
- 十万条Access数据表分页的两个解决方法
- sqlserver关于分页存储过程的优化【让数据库按我们的意思执行查询计划】
- 高效的mysql分页方法及原理
- asp又一个分页的代码例子
- SqlServer 2000、2005分页存储过程整理第1/3页
- ADO存取数据库时如何分页显示
- 透彻掌握ASP分页技术很详细的分析
- android中ListView数据刷新时的同步方法
- Android提高之ListView实现自适应表格的方法
- Android中实现水平滑动(横向滑动)ListView示例
- 一条SQL语句搞定Sql2000 分页
- 分页 SQLServer存储过程
- 实现SQL分页的存储过程代码
- sql分页查询几种写法
- SQL行号排序和分页(SQL查询中插入行号 自定义分页的另类实现)