SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新
本文出自:http://blog.csdn.net/dt235201314/article/details/75305222
一丶效果图(代码链接见文末)
图一:1.0版(埋坑版)
图二:2.0版(改进版)
二丶概述
RecyclerVIew的上拉加载,下拉刷新网上也有很多,包括github上的开源也有非常好用的,前面写好了RecyclerView实现首页,这里整理demo作为充实首页相关类容的开始
三丶功能介绍
1.完成上拉加载下来刷新功能
2.结合后台文档,开发情景讲解
3.踩坑记录,及问题分析,理解为什么这么做
四丶思路分析
先看接口文档:
分析:后台网络请求数据的参数是current,第一次请求current = 1,下拉刷新后请求current = current +1,当前页current< pages(总页数)才刷新
需求解决思路:
1.下来刷新用项目里常用的SwipeRefreshLayout,前面在RecyclerVIew实现主业有介绍,在下拉刷新后,重置网络请求参数为初始参数(=1)就OK。
2.上拉加载,前面学了RecyclerView多布局,设置一个“尾”布局,当current < pages时显示,提醒刷新,用RecyclerView.OnScrollListener监听,
layoutManager.findLastCompletelyVisibleItemPosition()获取最后的位置,当滑到最后时限数据请求....
五丶代码讲解
1.一个实体类解决数据问题
第一次加载20条数据,第二次加载10条
/** * <pre> * author : JinBiao * CSDN : http://my.csdn.net/DT235201314 * time : 2017/07/07 * desc : RecyclerView数据填充类 * version: 1.0 * </pre> */ public class RecyclerEntity extends AbsBaseEntity { private String category; //品类 private int pages; //页数 private int sales; //销量 private int allPage = 5; @Override public void parseData() { } public List<RecyclerEntity> parseData(int pages , String category ) { List<RecyclerEntity> list = new ArrayList<>(); if(pages == 1){ for(int i= 0;i<=19;i++){ RecyclerEntity recyclerEntity = new RecyclerEntity(); Random r = new Random(); recyclerEntity.setCategory(category); recyclerEntity.setPages(pages); recyclerEntity.setSales(r.nextInt(100)); list.add(recyclerEntity); } }else { for(int i= 0;i<=9;i++){ RecyclerEntity recyclerEntity = new RecyclerEntity(); Random r = new Random(); recyclerEntity.setCategory(category); recyclerEntity.setPages(pages); recyclerEntity.setSales(r.nextInt(100)); list.add(recyclerEntity); } } return list; } @Override public String toString() { return "RecyclerEntity{" + "category='" + category + '\'' + ", pages=" + pages + ", sales=" + sales + '}'; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public int getPages() { return pages; } public void setPages(int pages) { this.pages = pages; } public int getSales() { return sales; } public void setSales(int sales) { this.sales = sales; } public int getAllPage() { return allPage; } public void setAllPage(int allPage) { this.allPage = allPage; } }
2.adapter(埋坑版的)
/** * <pre> * author : JinBiao * CSDN : http://my.csdn.net/DT235201314 * time : 2017/07/07 * desc : 报数页面下拉加载更多Adapter * version: 1.0 * </pre> */ public class UpDownRefreshAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int TYPE_ITEM = 0; private static final int TYPE_FOOTER = 1; private FirstFragment firstFragment; private List<RecyclerEntity> records; boolean isNeedSetBgColor = true; boolean isGone = true ; private DecimalFormat mFormat; /** * 构造方法,把用到的值带过来,数据,可控制尾部加载提醒是否显示 * @param firstFragment * @param records * @param isGone */ public UpDownRefreshAdapter(FirstFragment firstFragment, List<RecyclerEntity> records,boolean isGone ) { this.firstFragment = firstFragment; this.records = records; this.isGone = isGone; mFormat = new DecimalFormat("#,###.##"); } public interface OnItemClickListener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } private OnItemClickListener onItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } @Override public int getItemCount() { if (records != null){ return records.size() == 0 ? 0 : records.size() + 1; }else { return 0; } } @Override public int getItemViewType(int position) { if (position + 1 == getItemCount()) { return TYPE_FOOTER; } else { return TYPE_ITEM; } } /** * 绑定数据显示栏和尾部提醒加载栏的布局 * @param parent * @param viewType * @return */ @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_ITEM) { View view = LayoutInflater.from(firstFragment.getContext()).inflate(R.layout.up_down_refresh_data_layout, parent, false); return new ItemViewHolder(view); } else if (viewType == TYPE_FOOTER) { View view = LayoutInflater.from(firstFragment.getContext()).inflate(R.layout.item_foot, parent, false); return new FootViewHolder(view); } return null; } /** * 设置显示数据处理,控制布局背景等,底部提醒是否显示 * @param holder * @param position */ @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { if (holder instanceof ItemViewHolder) { if (records != null) { RecyclerEntity recyclerEntity = records.get(position); ((ItemViewHolder) holder).tv_category.setText(recyclerEntity.getCategory()); ((ItemViewHolder) holder).tv_page.setText(mFormat.format(Double.valueOf(recyclerEntity.getPages()))); ((ItemViewHolder) holder).tv_sales.setText(mFormat.format(Double.valueOf(recyclerEntity.getSales()))); if (isNeedSetBgColor == false) { ((ItemViewHolder) holder).ll_recyclerData_item.setBackgroundColor(Color.parseColor("#ffffff")); } else { ((ItemViewHolder) holder).ll_recyclerData_item.setBackgroundColor(Color.parseColor("#fafafa")); } isNeedSetBgColor = !isNeedSetBgColor; } if (onItemClickListener != null) { holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = holder.getLayoutPosition(); onItemClickListener.onItemClick(holder.itemView, position); } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { int position = holder.getLayoutPosition(); onItemClickListener.onItemLongClick(holder.itemView, position); return false; } }); } } if (holder instanceof FootViewHolder) { if (isGone) { ((FootViewHolder) holder).ll_item_loading.setVisibility(View.GONE); } } } static class ItemViewHolder extends RecyclerView.ViewHolder { public TextView tv_category,tv_page,tv_sales; public LinearLayout ll_recyclerData_item; public ItemViewHolder(View view) { super(view); tv_category = (TextView) view.findViewById(R.id.tv_category); tv_page = (TextView) view.findViewById(R.id.tv_page); tv_sales = (TextView) view.findViewById(R.id.tv_sales); ll_recyclerData_item = (LinearLayout) view.findViewById(R.id.ll_recyclerData_item); } } static class FootViewHolder extends RecyclerView.ViewHolder { public LinearLayout ll_item_loading; public FootViewHolder(View view) { super(view); ll_item_loading = (LinearLayout) view.findViewById(R.id.ll_item_loading); } } }
3.fragment(埋坑版)
第一次加载显示20条,后面显示第10条(重新请求,坑1),下拉刷新重置。
public void getEntity() { if(isFirst){ current = 1; records = recyclerEntity.parseData(current,"全部"); }else{ List list = new ArrayList(); list.addAll(records); list.addAll(recyclerEntity.parseData(current,"全部")); records = list; } isLoading = (Integer.valueOf(pages) > Integer.valueOf(current)); upDownRefreshAdapter= new UpDownRefreshAdapter(this,records,!isLoading); recyclerView.setAdapter(upDownRefreshAdapter); addScrollListener(); mSwipeRefreshLayout.setRefreshing(false); } @Override public void onRefresh() { isFirst = true; getEntity(); mSwipeRefreshLayout.setRefreshing(false); }
上面isLoading是用来判断是否加载,isFirst判断是否第一次加载,addScrollListener是滑动监听
private void addScrollListener () { if (mOnScrollListener != null) { recyclerView.removeOnScrollListener(mOnScrollListener); } mOnScrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) recyclerView.getLayoutManager()) { @Override public void onLoadMore(int currentPage) { isFirst = false; if (isLoading) { new Handler().postDelayed(new Runnable() { @Override public void run() { current = current + 1 ; getEntity(); } }, 500); } } }; recyclerView.addOnScrollListener(mOnScrollListener); }这里监听加载的话就个重新获取下一页数据,mOnScollListener是专门的滑动监听继承类
layoutManager.findLastCompletelyVisibleItemPosition()获取最后的位置,当显示倒数第二条数据就加载,这里-1,也是可以的,-2就是提前做加载
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener { public static String TAG = EndlessRecyclerOnScrollListener.class .getSimpleName(); private int previousTotal = 0; private boolean loading = true; int lastCompletelyVisiableItemPosition, visibleItemCount, totalItemCount; private int currentPage = 1; private LinearLayoutManager mLinearLayoutManager; public EndlessRecyclerOnScrollListener( LinearLayoutManager linearLayoutManager) { this.mLinearLayoutManager = linearLayoutManager; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); visibleItemCount = recyclerView.getChildCount(); totalItemCount = recyclerView.getLayoutManager().getItemCount(); LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); lastCompletelyVisiableItemPosition =layoutManager.findLastCompletelyVisibleItemPosition(); if (loading) { if (totalItemCount > previousTotal) { loading = false; previousTotal = totalItemCount; } } if (!loading && (visibleItemCount > 0) && (lastCompletelyVisiableItemPosition >= totalItemCount - 2)) { currentPage++; onLoadMore(currentPage); loading = true; } } public abstract void onLoadMore(int currentPage); }
到这里,OK,埋坑版的就说到这里
测试发现,每次刷新后数据都会重新回到顶部。。。
。。。
错误分析:
原来,每次加载数据都是重新new的adapter,不回才怪,大神提醒要使用upDownRefreshAdapter.notifyDataSetChanged()提醒改变数据;
。。。
改完发现没效果,这是怎么回事,,看看notifyDataSetChanged()
原来new Adapter的时候,传入了数据对象的地址,数据变化,但对象的地址不会变,而你调用notifyDataChanged方法,重新调用了getSize、getView方法,虽然你没有告诉它数据变了,但是它存储的是对象地址,根据地址获取到这个对象,所以重绘页面的时候是变化后的数据
records,压根就没有初始化,从来就是复制,所以值一直没变化,初始化果然就OK了
如还有疑问参考如下文章
改完后,又有了第二个问题,控制尾部刷新提醒的又没有效果,再看notifyDataChanged方法
只关注数据的变化,也就是不管另一个参数的变化。
OK了,自己set咯(任何数据的变化最后都会走到set方法)
填坑版fragment
/** * SwipeRefreshLayout+RecyclerView实现下拉刷新上拉自动加载 */ public class FirstFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { /** * 下拉刷新view */ private SwipeRefreshLayout mSwipeRefreshLayout; private RecyclerView recyclerView; private View view; boolean isLoading = true; /** * 当前页 */ private int current; /** * 总页数 */ private int pages = 4; private List<RecyclerEntity> records = new ArrayList<>(); boolean isFirst = true; private UpDownRefreshAdapter upDownRefreshAdapter; private RecyclerEntity recyclerEntity = new RecyclerEntity(); private EndlessRecyclerOnScrollListener mOnScrollListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.first_fragment, container, false); intView(); return view; } public void intView() { mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout); recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); final LinearLayoutManager layoutManager = new LinearLayoutManager(this.getActivity()); recyclerView.setLayoutManager(layoutManager); getEntity(); mSwipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary, R.color.blueLight, R.color.btn_cm_bg_pressed, R.color.withe); mSwipeRefreshLayout.setOnRefreshListener(this); addScrollListener(); } private void addScrollListener () { if (mOnScrollListener != null) { recyclerView.removeOnScrollListener(mOnScrollListener); } mOnScrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) recyclerView.getLayoutManager()) { @Override public void onLoadMore(int currentPage) { isFirst = false; if (isLoading) { new Handler().postDelayed(new Runnable() { @Override public void run() { current = current + 1 ; getEntity(); } }, 500); } } }; recyclerView.addOnScrollListener(mOnScrollListener); } public void getEntity() { if(isFirst){ current = 1; records.clear(); records.addAll( recyclerEntity.parseData(current,"全部")); upDownRefreshAdapter= new UpDownRefreshAdapter(this,records); recyclerView.setAdapter(upDownRefreshAdapter); }else{ records.addAll(recyclerEntity.parseData(current,"全部")); upDownRefreshAdapter.notifyDataSetChanged(); } isLoading = (Integer.valueOf(pages) > Integer.valueOf(current)); upDownRefreshAdapter.setGone(!isLoading); addScrollListener(); mSwipeRefreshLayout.setRefreshing(false); } @Override public void onRefresh() { isFirst = true; getEntity(); mSwipeRefreshLayout.setRefreshing(false); } }填坑版adapter也就添加了一个set方法来获取是否显示尾部的提醒
SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新就分享到这里。
如果对你有帮助,点赞关注一下博主star我的github都是对我最大的支持
源码下载欢迎Star(updating):https://github.com/JinBoy23520/CoderToDeveloperByTCLer