热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

AndroidRecyclerView上拉加载更多及下拉刷新功能的实现方法

这篇文章主要介绍了AndroidRecyclerView上拉加载更多及下拉刷新的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下

RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈。

先贴图上来看看:

这里写图片描述 

 
这里写图片描述

使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式:

1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现。

2.自定义的里面带有RecyleView的控件。

使用RecycleView很不好添加头部,之前在使用listview当中自己可以添加header和bootm,但是RecycleView好像不是那么的好操作。对于第一种使用系统自带的Android.support.v4.widget.SwipeRefreshLayout来实现的,也很好用,但是产品一般不要这种下拉刷新,为了让自己显得牛逼,他一般会搞一个自己的带有动画,这就比较扯淡了。。。所以就只能用方法2了。

大致说一哈方法2的实现方式,父布局为ViewGroup,里面添加View第一个为控件为header第二个控件为RecycleView,至于最底部的下拉加载更多试图,通过RecycleViw的Adapter来添加。

有了思路就搞起来:

package com.krain.srecyclerview.fruitview;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.krain.srecyclerview.R;
/**
* Created by dafuShao on 2016/9/9 0009.
*
*/
public class ElizabethView extends FrameLayout {
private ImageView imageView;
private AnimationDrawable animationDrawable;
public ElizabethView(Context context) {
super(context);
initview(context);
}
public ElizabethView(Context context, AttributeSet attrs) {
super(context, attrs);
initview(context);
}
public ElizabethView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initview(context);
}
private void initview(Context context){
View view= LayoutInflater.from(context).inflate(R.layout.elizabeth_item,null);
imageView=(ImageView) view.findViewById(R.id.elizabeth_im);
animatiOnDrawable= (AnimationDrawable) imageView.getBackground();
addView(view);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
//开始动画
public void startAnim(){
animationDrawable.start();
}
//停止动画
public void stopAnim(){
animationDrawable.stop();
}
}

这是头部控价很简单里面有一个小的臭蛋眼睛左右挤一哈的效果就不贴图了。

下面是自定义的包含RecyclerView的控件代码如下:

package com.krain.srecyclerview.srecyclerview;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;
import com.krain.srecyclerview.R;
import com.krain.srecyclerview.fruitview.ElizabethView;
public class SRecyclerView extends ViewGroup {
Context context;
RecyclerView mRecyclerView;
ElizabethView mHeaderView;
TextView mFootViewTips;//footview的文字显示
AdapterWrapper mAdapter;
boolean mIsTop = true;//是否滑动到了最顶部
RecyclerView.LayoutManager mLayoutManager;
int mLastVisibleItem;
int mFirstVisibleItem;
OnRecyclerStatusChangeListener mRecyclerChangeListener;
int mStatus;//当前状态
int mHeadviewHeight;//headview的高度
Scroller mScroller;
int mFristScollerY;//最开始的getscrolly
boolean mHasFooter;//是否有上拉加载的功能
boolean mShowFootVisible;//是否显示FOOTERview在mHasFooter为true的时候生效
boolean mHasRefresh = true;//是否支持下拉刷新
private final int DEFAULT_MIN_PAGEINDEX = 1;//默认最小的页数
int mMaxPage = DEFAULT_MIN_PAGEINDEX;//分页的总页数
int mCurrentPage = DEFAULT_MIN_PAGEINDEX;//当前的页数,从1开始
private final int STATUS_NORMAL = 0, STATUS_REFRESH = 1, STATUS_LOAD = 2;
private final int MSG_LOAD_COMPLETE = 1, MSG_REFRESH_COMPLETE = 0;//handle的常量
private final int DELAY_LOAD_COMPLETE = 1000, DELAY_REFRESH_COMPLETE = 1000;//加载完成延时回收的时间
public SRecyclerView(Context context) {
super(context);
init(context);
}
public SRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 设置最大页数
*
* @param maxPage
*/
public void setMaxPage(int maxPage) {
this.mMaxPage = maxPage;
}
/**
* 是否支持上拉加载
*
* @param hasLoadmore
*/
public void setLoadmore(boolean hasLoadmore) {
mHasFooter = hasLoadmore;
}
public void setRecyclerViewLayoutManage(RecyclerView.LayoutManager mLayoutManage){
this.mLayoutManager=mLayoutManage;
}
/**
* 关闭下拉刷新功能
*/
public void disableRefresh() {
mHasRefresh = false;
}
public void setAdapter(BaseRecyclerViewAdapter adapter) {
int height = 0;
if (mMaxPage == DEFAULT_MIN_PAGEINDEX) {
mHasFooter = false;
}
mAdapter = new AdapterWrapper(context, adapter);
mRecyclerView.setAdapter(mAdapter);
}
private int getViewHeight(View view) {
int measure = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measure, measure);
return view.getMeasuredHeight();
}
/**
* 获取viewgroup里面的recyclerview
*
* @return
*/
public RecyclerView getRecyclerView() {
return mRecyclerView;
}
public void setOnRecyclerChangeListener(OnRecyclerStatusChangeListener listener) {
mRecyclerChangeListener = listener;
}
/**
* 设置RecyclerView添加、删除Item的动画
*
* @param animator
*/
public void setItemAnimator(RecyclerView.ItemAnimator animator) {
mRecyclerView.setItemAnimator(animator);
}
public void notifyDataSetChanged() {
mStatus = STATUS_NORMAL;//重新set数据代表loadmore结束了,此时恢复成普通状态
mAdapter.notifyDataSetChanged();
}
public void notifyDataInsert(int positionStart, int itemCount) {
mStatus = STATUS_NORMAL;//重新set数据代表loadmore结束了,此时恢复成普通状态
mAdapter.notifyItemRangeInserted(positionStart, itemCount);
}
public void notifyDataRemove(int position) {
mStatus = STATUS_NORMAL;//重新set数据代表loadmore结束了,此时恢复成普通状态
mAdapter.notifyItemRemoved(position);
}
/**
* 初始化操作
*
* @param context
*/
void init(Context context) {
this.cOntext= context;
mScroller = new Scroller(context);
// if (mLayoutManager!=null){
// addChildView(context,mLayoutManager);
// }else{
// addChildView(context, new LinearLayoutManager(context));
// }
addChildView(context);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
/**
* 增加子View
*
* @param context
*/
void addChildView(Context context, RecyclerView.LayoutManager mLayoutManager) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mHeaderView = new ElizabethView(context);
mRecyclerView = new RecyclerView(context);
addView(mHeaderView);
addView(mRecyclerView);
//mLayoutManager = new LinearLayoutManager(context);
// mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
// 设置Item增加、移除默认动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addOnScrollListener(onScrollListener);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutParams(params);
}
void addChildView(Context contex) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mHeaderView = new ElizabethView(context);
mRecyclerView = new RecyclerView(context);
addView(mHeaderView);
addView(mRecyclerView);
//mLayoutManager = new LinearLayoutManager(context);
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
// 设置Item增加、移除默认动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addOnScrollListener(onScrollListener);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutParams(params);
}
/**
* 屏蔽Recyclerview的触摸事件(下拉刷新的时候)
*/
float lastY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
lastY = ev.getRawY();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
return false;
case MotionEvent.ACTION_MOVE:
if (mHasRefresh && mIsTop && ev.getRawY() > lastY)
return true;
break;
}
return false;
}
float offsetY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
offsetY = Math.abs(event.getRawY() - lastY); //Y差值绝对值
if (offsetY > 0)
scrollToOffset(offsetY);
else {
mIsTop = false;
}
break;
case MotionEvent.ACTION_UP:
if (getScrollY() <= 0) {
doRefresh();
mHeaderView.stopAnim();
mHeaderView.startAnim();
} else complete();
break;
}
return super.onTouchEvent(event);
}
/**
* 滚动这个view到手滑动的位置
*
* @param offsetY Y轴偏移量
*/
void scrollToOffset(float offsetY) {
//假如正在刷新并且现在的scrolly和初始值一样的时候,代表准备下拉开始刷新,并执行一次only
if (getScrollY() == mFristScollerY && mRecyclerChangeListener != null)
mRecyclerChangeListener.startRefresh();
int value = Math.round(offsetY / 2.0F);
value = mFristScollerY - value;
scrollTo(0, value);
}
/**
* 执行刷新操作,移动到header刚出来的位置
*/
void doRefresh() {
mStatus = STATUS_REFRESH;
int currentY = getScrollY();
mScroller.startScroll(0, currentY, 0, (mFristScollerY - mHeadviewHeight) - currentY);
invalidate();
if (mRecyclerChangeListener != null) mRecyclerChangeListener.onRefresh();
handler.sendEmptyMessageDelayed(MSG_REFRESH_COMPLETE, DELAY_REFRESH_COMPLETE);
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == MSG_LOAD_COMPLETE) {
View footview = mAdapter.getFootView();
if (footview != null)
mRecyclerView.smoothScrollBy(0, -footview.getMeasuredHeight());
} else if (msg.what == MSG_REFRESH_COMPLETE)
complete();
}
};
/**
* header返回原处完全隐藏
*/
public void complete() {
mCurrentPage = DEFAULT_MIN_PAGEINDEX;//完成之后当前的page恢复默认值
if (mFootViewTips != null)
mFootViewTips.setText(context.getString(R.string.loading));//更改foot提示为正在加载中
if (mRecyclerChangeListener != null) mRecyclerChangeListener.refreshComplete();
mStatus = STATUS_NORMAL;
int currentY = getScrollY();
mScroller.startScroll(0, currentY, 0, mFristScollerY - currentY);
mHeaderView.stopAnim();
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
for (int i = 0; i  max) {
max = value;
}
}
return max;
}
private class AdapterWrapper extends RecyclerView.Adapter {
private static final int TYPE_ITEM = 0;
private static final int TYPE_FOOTER = 1;
private RecyclerView.Adapter mAdapter;
private Context mContext;
View footer;
public AdapterWrapper(Context context, RecyclerView.Adapter wrappedAdapter) {
this.mCOntext= context;
this.mAdapter = wrappedAdapter;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = null;
switch (viewType) {
case TYPE_ITEM:
holder = mAdapter.onCreateViewHolder(parent, viewType);
break;
case TYPE_FOOTER:
footer = LayoutInflater.from(mContext).inflate(R.layout.lib_recyle_footview, null);
LinearLayout linearLayout= (LinearLayout) footer.findViewById(R.id.loading_layout);
StaggeredGridLayoutManager.LayoutParams layoutParams =
new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setFullSpan(true);
linearLayout.setLayoutParams(layoutParams);
holder = new FooterViewHolder(footer);
break;
}
return holder;
}
public View getFootView() {
return footer;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (!mHasFooter || position + 1 != getItemCount()) {
mAdapter.onBindViewHolder(holder, position);
}
}
@Override
public int getItemCount() {
return mShowFootVisible &#63; mAdapter.getItemCount() + 1 : mAdapter.getItemCount();
}
@Override
public int getItemViewType(int position) {
if (mShowFootVisible && position + 1 == getItemCount()) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
private class FooterViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public TextView tvLoading;
public LinearLayout llyLoading;
public FooterViewHolder(View itemView) {
super(itemView);
progressBar = (ProgressBar) itemView.findViewById(R.id.progress_loading);
tvLoading = (TextView) itemView.findViewById(R.id.footer_tips);
llyLoading = (LinearLayout) itemView.findViewById(R.id.loading_layout);
}
}
}
}

最后还有一个就是Adapter的代码:

package com.krain.srecyclerview.srecyclerview;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter {
private OnItemClickLisener mItemListener;
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
View view = getItemView(viewType, parent);
VH vh = getViewHolder(view);
view.setOnClickListener(new OnRecyclerAdapterclickListener(vh, viewType));
view.setOnLongClickListener(new OnRecyclerAdapterclickListener(vh, viewType));
return vh;
}
public abstract VH getViewHolder(View itemView);
/**
* 返回item的view
*
* @return
*/
public abstract View getItemView(int viewType, ViewGroup parent);
/**
* 返回Adapter每个itemn的数据 可选
*/
public Object getItem(int position) {
return null;
}
/**
* item点击事件接口
*
* @param mItemListener
*/
public void setOnItemListener(OnItemClickLisener mItemListener) {
this.mItemListener = mItemListener;
}
@Override
public abstract void onBindViewHolder(VH holder, int position);
@Override
public abstract int getItemCount();
class OnRecyclerAdapterclickListener implements View.OnClickListener, View.OnLongClickListener {
VH viewholder;
int viewType;
public OnRecyclerAdapterclickListener(VH viewholder, int viewType) {
this.viewholder = viewholder;
this.viewType = viewType;
}
@Override
public void onClick(View v) {
if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) {
mItemListener.onItemClick(viewholder.getAdapterPosition(), viewType, viewholder, v);
}
}
@Override
public boolean onLongClick(View v) {
if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) {
mItemListener.onItemLongClick(viewholder.getAdapterPosition(), viewType, viewholder, v);
}
return false;
}
}
}

需要注意一哈:

如果想改变Reciview的布局方式在这个修改

void addChildView(Context contex) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mHeaderView = new ElizabethView(context);
mRecyclerView = new RecyclerView(context);
addView(mHeaderView);
addView(mRecyclerView);
//mLayoutManager = new LinearLayoutManager(context);
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
// 设置Item增加、移除默认动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addOnScrollListener(onScrollListener);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutParams(params);
}

mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);这一行就是了改成对应的就可以了,这儿没有封装过几天封装一个出来。

还需要注意一哈就是在滑动判断是不是最后一行,如果是瀑布流就要注意哈判断的方式和其他的不一样,

if (mLayoutManager instanceof StaggeredGridLayoutManager) {
//因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组
//得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了
int[] lastPositiOns= new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()];
((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions);
mLastVisibleItem = findMax(lastPositions);
mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0];
setFootviewVisible();
}

因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组,得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了再去设置最后一行加载更多的显示。

以上所述是小编给大家介绍的Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的,在此也非常感谢大家对网址的支持!


推荐阅读
  • 使用TabActivity实现Android顶部选项卡功能
    本文介绍如何通过继承TabActivity来创建Android应用中的顶部选项卡。通过简单的步骤,您可以轻松地添加多个选项卡,并实现基本的界面切换功能。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 数据类型--char一、char1.1char占用2个字节char取值范围:【0~65535】char采用unicode编码方式char类型的字面量用单引号括起来char可以存储一 ... [详细]
  • 本文详细介绍了iOS应用的生命周期,包括各个状态及其转换过程中的关键方法调用。 ... [详细]
  • 项目风险管理策略与实践
    本文探讨了项目风险管理的关键环节,包括风险管理规划、风险识别、风险分析(定性和定量)、风险应对策略规划及风险控制。旨在通过系统的方法提升项目成功率,减少不确定因素对项目的影响。 ... [详细]
  • 探索AI智能机器人自动盈利系统的构建
    用户可通过支付198元押金及30元设备维护费租赁AI智能机器人,推荐他人加入可获得相应佣金。随着推荐人数的增加,用户将逐步解锁更高版本,享受更多收益。 ... [详细]
  • 本文将从基础概念入手,详细探讨SpringMVC框架中DispatcherServlet如何通过HandlerMapping进行请求分发,以及其背后的源码实现细节。 ... [详细]
  • Windows操作系统提供了Encrypting File System (EFS)作为内置的数据加密工具,特别适用于对NTFS分区上的文件和文件夹进行加密处理。本文将详细介绍如何使用EFS加密文件夹,以及加密过程中的注意事项。 ... [详细]
  • 回顾两年前春节期间的一个个人项目,该项目原本计划参加竞赛,但最终作为练习项目完成。独自完成了从编码到UI设计的全部工作,尽管代码量不大,但仍有一定的参考价值。本文将详细介绍该项目的背景、功能及技术实现。 ... [详细]
  • 如何在PHP中安装Xdebug扩展
    本文介绍了如何从PECL下载并编译安装Xdebug扩展,以及如何配置PHP和PHPStorm以启用调试功能。 ... [详细]
  • Android与JUnit集成测试实践
    本文探讨了如何在Android项目中集成JUnit进行单元测试,并详细介绍了修改AndroidManifest.xml文件以支持测试的方法。 ... [详细]
  • 本文探讨了在一个物理隔离的环境中构建数据交换平台所面临的挑战,包括但不限于数据加密、传输监控及确保文件交换的安全性和可靠性。同时,作者结合自身项目经验,分享了项目规划、实施过程中的关键决策及其背后的思考。 ... [详细]
  • importjava.io.*;importjava.util.*;publicclass五子棋游戏{staticintm1;staticintn1;staticfinalintS ... [详细]
  • 解决Visual Studio Code中PHP Intelephense误报问题
    PHP作为一种高度灵活的编程语言,其代码结构可能导致Intelephense插件在某些情况下报告不必要的错误或警告。自1.3.3版本起,Intelephense引入了多个配置选项,允许用户根据具体的工作环境和编程风格调整这些诊断信息的显示。 ... [详细]
  • 心理学经典:《思考致富》
    《思考致富》是由美国著名成功学大师拿破仑·希尔撰写的一部重要著作,该书基于希尔长达20年的深入研究和访谈,探讨了个人成功的核心要素。书中不仅揭示了成功的关键,还提供了一系列实用的方法和策略。 ... [详细]
author-avatar
手机用户2502941585_336
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有