Android实现RecyclerView下拉刷新效果

本文为大家分享了Android实现RecyclerView下拉刷新效果的具体代码,供大家参考,具体内容如下

思路

RealPullRefreshView继承了一个LinearLayout 里面放置了一个刷新头布局,将其margin_top设置为负的刷新头的高度的 再添加一个RecyclerView 触摸事件分发机制,当在特定条件下让RealPullRefreshView拦截触摸事件,否则的话,不拦截,让RecyclerView自己去处理触摸事件 在手指下拉时,定义好不同的状态STATE,在不同状态下,处理不同的显示,这里讲不同状态下的刷新头如何显示,抽象为一个接口,用户可以实现这个接口,自定义刷新头的布局和动画 加载更多的功能是利用RecyclerView的多type布局实现的 难点在于触摸事件的拦截,和认真处理各种滑动的问题

使用

xml

<com.example.apple.quickdemo.realview.view.RealPullRefreshView android:id="@+id/real_pull_refresh_view" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:refresh_header_view="@layout/headerview"/>

这是headerview

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ff0" android:orientation="horizontal" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@color/colorAccent" android:visibility="visible" android:id="@+id/tv" android:gravity="center" android:text="下拉刷新" android:textSize="21sp"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" android:id="@+id/iv" /> </LinearLayout>

代码

mRealPullRefreshView.setLayoutManager(mLayoutManager); mRealPullRefreshView.setAdapter(mMyAdapte); //用户可以自定义自己的刷新头布局和动画 //mRealPullRefreshView.setOnPullShowViewListener(new GifOnPullShowViewListerner(mRealPullRefreshView)); mRealPullRefreshView.setOnPullListener(new RealPullRefreshView.OnPullListener() { @Override public void onRefresh() { mHandler.postDelayed(new Runnable() { @Override public void run() { mBodies.add(0, new Body("新数据"+i++,100)); mRealPullRefreshView.refreshFinish(); } }, 3000); } @Override public void onLoadMore() { final List<Body> more=new ArrayList<Body>(); mHandler.postDelayed(new Runnable() { @Override public void run() { for (int i = 0; i < 3; i++) { more.add(new Body("more+++"+i,100)); } mBodies.addAll(more); mRealPullRefreshView.loadMreFinish(); } }, 1500); } });

自定义刷新头布局和动画

package com.example.apple.quickdemo.realview.show; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.example.apple.quickdemo.R; import com.example.apple.quickdemo.realview.view.RealPullRefreshView; /** * Created by apple on 2017/7/9. */ public class ImplOnPullShowViewListener implements RealPullRefreshView.OnPullShowViewListener { private TextView mTv; private ImageView mIv; private ObjectAnimator mAni; View mHeaderView; public ImplOnPullShowViewListener(RealPullRefreshView realPullRefreshView) { mHeaderView = realPullRefreshView.getRefreshHeaderView(); mTv = (TextView) mHeaderView.findViewById(R.id.tv); mIv = (ImageView) mHeaderView.findViewById(R.id.iv); mAni = ObjectAnimator.ofFloat(mIv, "rotation", -15, 15).setDuration(300); mAni.setRepeatCount(ValueAnimator.INFINITE); mAni.setRepeatMode(ValueAnimator.REVERSE); } @Override public void onPullDownRefreshState(int scrollY, int headviewHeight,int deltaY) { mTv.setText("下拉刷新"); float f = -((float) scrollY / (float) headviewHeight); Log.e("tag", f+ ""); Log.e("tag", -scrollY + "scrollY"); mIv.setScaleX(f); mIv.setScaleY(f); } @Override public void onReleaseRefreshState(int scrollY, int deltaY) { mTv.setText("松手刷新"); } @Override public void onRefreshingState() { mTv.setText("正在刷新"); mIv.setScaleX(1.0f); mIv.setScaleY(1.0f); mAni.start(); } @Override public void onDefaultState() { if (mAni.isRunning()){ mAni.end(); mIv.setRotation(0); } } }

源码

package com.example.apple.quickdemo.realview.view; import android.content.Context; import android.content.res.TypedArray; import android.support.annotation.Nullable; 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.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.Scroller; import android.widget.Toast; import com.example.apple.quickdemo.R; import com.example.apple.quickdemo.realview.show.ImplOnPullShowViewListener; import static android.content.ContentValues.TAG; /** * Created by apple on 2017/7/7. * 下拉刷新 */ public class RealPullRefreshView extends LinearLayout { private int mTouchSlop; // 分别记录上次滑动的坐标 private int mLastX = 0; private int mLastY = 0; // 分别记录上次滑动的坐标(onInterceptTouchEnvent) private int mLastXIntercept = 0; private int mLastYIntercept = 0; private Scroller mScroller; private VelocityTracker mVelocityTracker; private RecyclerView.Adapter mAdapter; public RecyclerView getRecyclerView() { return mRecyclerView; } private RecyclerView mRecyclerView; private int DEFAULT = 0; private final int PULL_DOWN_REFRESH = 1; private final int RELEASE_REFRESH = 2; private final int REFRESHING = 3; private final int LOAD_MORE = 4; private int STATE = DEFAULT; private int rfreshHeaderWidth; private int refreshHeadviewHeight; private OnPullListener mOnPullListener; private View mRefreshHeaderView; private RecyclerView.LayoutManager mLayoutManager; int refreshHeadviewId; public void setLayoutManager(RecyclerView.LayoutManager manager) { this.mLayoutManager = manager; mRecyclerView.setLayoutManager(mLayoutManager); } public void setAdapter(RecyclerView.Adapter adapter) { this.mAdapter = adapter; mRecyclerView.setAdapter(mAdapter); } public View getRefreshHeaderView() { return mRefreshHeaderView; } public void setOnPullShowViewListener(OnPullShowViewListener onPullShowViewListener) { mOnPullShowViewListener = onPullShowViewListener; } private OnPullShowViewListener mOnPullShowViewListener; public void setOnPullListener(OnPullListener onPullListener) { mOnPullListener = onPullListener; } public RealPullRefreshView(Context context) { super(context); initView(context); } public RealPullRefreshView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initAttrs(context, attrs); // ★★★★★一个坑initAttrs方法里的typedArray去获取属性时,第一次获取的属性全是0,他会马上重走一次构造方法,再次获取一次,才能获得正确的值 // 如果第一次获取的值为0,则不去initView if (refreshHeadviewId != 0) { initView(context); } } public RealPullRefreshView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(context, attrs); if (refreshHeadviewId != 0) { initView(context); } } private void initAttrs(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RealPullRefreshView); try { refreshHeadviewId = typedArray.getResourceId(R.styleable.RealPullRefreshView_refresh_header_view, 0); } finally { typedArray.recycle(); } } private void initView(Context context) { mScroller = new Scroller(getContext()); mVelocityTracker = VelocityTracker.obtain(); // 添加headerview // ★ ★ ★ ★ ★ 注意不要用这个方法inflate布局,会导致layout的所有属性失效,height、width、margin // 原因见 http://blog.csdn.net/zhaokaiqiang1992/article/details/36006467 // ★ ★ ★ ★ ★ mRefreshHeaderView = mInflater.inflate(R.layout.headerview, null); mRefreshHeaderView = LayoutInflater.from(context).inflate(refreshHeadviewId, this, false); addView(mRefreshHeaderView); // } // 以下代码主要是为了设置头布局的marginTop值为-headerviewHeight // 注意必须等到一小会才会得到正确的头布局宽高 postDelayed(new Runnable() { @Override public void run() { Log.e("q11", refreshHeadviewHeight + "qqqqqqqqqq " + mRefreshHeaderView.getHeight()); rfreshHeaderWidth = mRefreshHeaderView.getWidth(); refreshHeadviewHeight = mRefreshHeaderView.getHeight(); MarginLayoutParams lp = new LinearLayout.LayoutParams(rfreshHeaderWidth, refreshHeadviewHeight); lp.setMargins(0, -refreshHeadviewHeight, 0, 0); mRefreshHeaderView.setLayoutParams(lp); } }, 100); // 添加RecyclerView mRecyclerView = new RecyclerView(context); addView(mRecyclerView, LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); // 这里我提供了一个默认的显示效果,如果用户不使用mRealPullRefreshView.setOnPullShowViewListener的话,会默认使用这个 // 用户可以实现OnPullShowViewListener接口,去实现自己想要的显示效果 mOnPullShowViewListener = new ImplOnPullShowViewListener(this); setLoadMore(); } private void setLoadMore() { // 当目前的可见条目是所有数据的最后一个时,开始加载新的数据 mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int lastCompletelyVisibleItemPosition = -1; if (mLayoutManager instanceof LinearLayoutManager) { LinearLayoutManager manager = (LinearLayoutManager) mLayoutManager; lastCompletelyVisibleItemPosition = manager.findLastVisibleItemPosition(); } else if (mLayoutManager instanceof GridLayoutManager) { GridLayoutManager manager = (GridLayoutManager) mLayoutManager; lastCompletelyVisibleItemPosition = manager.findLastVisibleItemPosition(); } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) mLayoutManager; lastCompletelyVisibleItemPosition = manager.findLastVisibleItemPositions(new int[manager.getSpanCount()])[0]; } if (lastCompletelyVisibleItemPosition + 1 == mAdapter.getItemCount()) { if (mOnPullListener != null && STATE == DEFAULT) { STATE = LOAD_MORE; mOnPullListener.onLoadMore(); } } } }); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { boolean intercepted = false; int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { intercepted = false; /*if (STATE!=DEFAULT||STATE!=REFRESHING){ if (!mScroller.isFinished()) { mScroller.abortAnimation(); }}*/ break; } case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; int firstCompletelyVisibleItemPosition = -1; if (mLayoutManager instanceof LinearLayoutManager) { LinearLayoutManager manager = (LinearLayoutManager) mLayoutManager; firstCompletelyVisibleItemPosition = manager.findFirstCompletelyVisibleItemPosition(); } else if (mLayoutManager instanceof GridLayoutManager) { GridLayoutManager manager = (GridLayoutManager) mLayoutManager; firstCompletelyVisibleItemPosition = manager.findFirstCompletelyVisibleItemPosition(); } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) mLayoutManager; firstCompletelyVisibleItemPosition = manager.findFirstCompletelyVisibleItemPositions(new int[manager.getSpanCount()])[0]; } // ******************这里说明什么规则下,拦截,其余代码不要动了,其余代码指的是处理滑动冲突的代码*************** if (firstCompletelyVisibleItemPosition == 0 && deltaY > 0 && Math.abs(deltaY) > Math.abs(deltaX)) {//拉倒最顶部,继续往下拉,将拉出头布局,要父布局拦截 intercepted = true; } else if (getScrollY() < 0) {//表示头布局已经向下拉出来,头布局已经显示了,要父布局拦截 intercepted = true; } else if (deltaY < 0) { intercepted = false;//不要父布局拦截了 } else { intercepted = false;//不要父布局拦截了 } // ******************什么规则下,拦截*************** break; } case MotionEvent.ACTION_UP: { intercepted = false; break; } default: break; } Log.d(TAG, "intercepted=" + intercepted); mLastX = x; mLastY = y; mLastXIntercept = x; mLastYIntercept = y; return intercepted; } /** * 下面不同布局,不同的滑动需求 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { mVelocityTracker.addMovement(event); int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; } case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastX; int deltaY = y - mLastY; if (getScrollY() > 0) { //防止在正在刷新状态下,上拉出空白 } else if (getScrollY() <= 0 && getScrollY() > -refreshHeadviewHeight * 5) { // 最多下拉到头布局高度5倍的距离 scrollBy(0, -deltaY / 2); } if (getScrollY() > -refreshHeadviewHeight && STATE != REFRESHING) {//头布局显示不全时,为下拉刷新PULL_DOWN_REFRESH状态 STATE = PULL_DOWN_REFRESH; if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onPullDownRefreshState(getScrollY(), refreshHeadviewHeight, deltaY); } } if (getScrollY() < -refreshHeadviewHeight && STATE != REFRESHING) {//头布局完全显示时,为释放刷新RELEASE_REFRESH状态 STATE = RELEASE_REFRESH; if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onReleaseRefreshState(getScrollY(), deltaY); } } break; } case MotionEvent.ACTION_UP: { final int scrollY = getScrollY(); //松手时,根据所处的状态,让布局滑动到不同的地方,做不同的操作 switch (STATE) { case PULL_DOWN_REFRESH: STATE = DEFAULT; //头布局没有完全显示,完全隐藏头布局 smoothScrollBy(0, -scrollY); break; case RELEASE_REFRESH: STATE = REFRESHING; smoothScrollBy(0, -refreshHeadviewHeight - scrollY); if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onRefreshingState(); } if (mOnPullListener != null) { mOnPullListener.onRefresh(); } break; case REFRESHING: if (getScrollY() < -refreshHeadviewHeight) { smoothScrollBy(0, -refreshHeadviewHeight - scrollY); } else { smoothScrollBy(0, -scrollY); } break; } mVelocityTracker.clear(); break; } default: break; } mLastX = x; mLastY = y; return true; } /** * 当用户使用完下拉刷新回调时,需要调用此方法,将头不去隐藏,将STATE恢复 */ public void refreshFinish() { smoothScrollBy(0, 0 - getScrollY()); getRecyclerView().getAdapter().notifyDataSetChanged(); STATE = DEFAULT; if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onDefaultState(); } Toast.makeText(getContext(), "刷新成功!", Toast.LENGTH_SHORT).show(); } /** * 当用户使用完加载更多后回调时,需要调用此方法,将STATE恢复 */ public void loadMreFinish() { getRecyclerView().getAdapter().notifyDataSetChanged(); STATE = DEFAULT; Toast.makeText(getContext(), "加载成功了!", Toast.LENGTH_SHORT).show(); } /** * 在500毫秒内平滑地滚动多少像素点 * * @param dx * @param dy */ private void smoothScrollBy(int dx, int dy) { mScroller.startScroll(0, getScrollY(), 0, dy, 500); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } /** * 释放资源 */ @Override protected void onDetachedFromWindow() { mVelocityTracker.recycle(); super.onDetachedFromWindow(); } // *************** // ***************** /** * 回调接口 */ public interface OnPullListener { /** * 当下拉刷新正在刷新时,这时候可以去请求数据,记得最后调用refreshFinish()复位 */ void onRefresh(); /** * 当加载更多时 */ void onLoadMore(); } /** * 回调接口,可以通过下面的回调,自定义各种状态下的显示效果 * 可以根据下拉距离scrollY设计动画效果 */ public interface OnPullShowViewListener { /** * 当处于下拉刷新时,头布局显示效果 * * @param scrollY 下拉的距离 * @param headviewHeight 头布局高度 * @param deltaY moveY-lastMoveY,正值为向下拉 */ void onPullDownRefreshState(int scrollY, int headviewHeight, int deltaY); /** * 当处于松手刷新时,头布局显示效果 * * @param scrollY 下拉的距离 * @param deltaY moveY-lastMoveY,正值为向下拉 */ void onReleaseRefreshState(int scrollY, int deltaY); /** * 正在刷新时,页面的显示效果 */ void onRefreshingState(); /** * 默认状态时,页面显示效果,主要是为了复位各种状态 */ void onDefaultState(); } }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-10-11 21:03:47

Android实现RecyclerView下拉刷新效果的相关文章

Android使用PullToRefresh实现上拉加载和下拉刷新效果的代码_Android

在没给大家介绍正文之前,先给大家介绍展示下运行图,如果大家感觉还不错,请继续往下阅读: 相关阅读:分享Android中pullToRefresh的使用心得 项目已同步至:https://github.com/nanchen2251/pullToRefreshDemo 简单使用详情: 1)studio可以直接在app的module设置中直接进行搜索,但是有-的必须添上,而不能用空格代替,为了更加了解这个东西,我还是推荐大家去这里看看,奉上网址: https://github.com/chrisba

Android使用PullToRefresh实现上拉加载和下拉刷新效果的代码

在没给大家介绍正文之前,先给大家介绍展示下运行图,如果大家感觉还不错,请继续往下阅读: 相关阅读:分享Android中pullToRefresh的使用心得 项目已同步至:https://github.com/nanchen2251/pullToRefreshDemo 简单使用详情: 1)studio可以直接在app的module设置中直接进行搜索,但是有-的必须添上,而不能用空格代替,为了更加了解这个东西,我还是推荐大家去这里看看,奉上网址: https://github.com/chrisba

Android RecyclerView下拉刷新和上拉加载更多_Android

今天终于有点时间,来写了一下: 为RecyclerView实现下拉刷新和上拉加载更多.今天会在前面的两篇文章的基础上: RecyclerView系列之(1):为RecyclerView添加Header和Footer RecyclerView系列之(2):为RecyclerView添加分隔线 继续讲述RecyclerView中一些常用组件的实现下拉刷新和上拉加载更多的功能. 在现在的Android手机应用中,几乎每一个APP都有下拉刷新和上拉加载更多的功能,它们的重要性不言而喻. 先不多说,先看效

Android仿百度外卖自定义下拉刷新效果_Android

现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前两天订饭的时候不经意间看到了"百度外卖"的下拉刷新,今天的主题就是它–自定义下拉刷新动画. 看一下实现效果吧: 动画 我们先来看看Android中的动画吧: Android中的动画分为三种: Tween动画,这一类的动画提供了旋转.平移.缩放等效果. Alpha – 淡入淡出 Scale – 缩放效果 Roate – 旋转效果 Translate – 平移效果 Frame动画(帧动画),这一类动画可以创建一个D

Android自定义实现顶部粘性下拉刷新效果

本文实例为大家分享了Android实现顶部粘性下拉刷新效果的具体代码,供大家参考,具体内容如下 学习:视频地址 activity_view_mv代码 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://sche

Android仿百度外卖自定义下拉刷新效果

现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前两天订饭的时候不经意间看到了"百度外卖"的下拉刷新,今天的主题就是它–自定义下拉刷新动画. 看一下实现效果吧: 动画 我们先来看看Android中的动画吧: Android中的动画分为三种: Tween动画,这一类的动画提供了旋转.平移.缩放等效果. Alpha – 淡入淡出 Scale – 缩放效果 Roate – 旋转效果 Translate – 平移效果 Frame动画(帧动画),这一类动画可以创建一个D

Android LRecyclerView实现下拉刷新,滑动到底部自动加载更多

很想给大家分享这个开源项目,但是由于工作的关系,没有抽出空,但还是趁着工作间隙写下了这篇博客. 简介 LRecyclerView是支持addHeaderView. addFooterView.下拉刷新.分页加载数据的RecyclerView. 它对 RecyclerView 控件进行了拓展,给RecyclerView增加HeaderView.FooterView,并且不需要对你的Adapter做任何修改. 主要功能 下拉刷新.滑动到底部自动加载下页数据: 可以方便添加Header和Footer:

Android自定义控件实战——下拉刷新控件终结者:PullToRefreshLayout

转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38340701            说到下拉刷新控件,网上版本有很多,很多软件也都有下拉刷新功能.有一个叫XListView的,我看别人用过,没看过是咋实现的,看这名字估计是继承自ListView修改的,不过效果看起来挺丑的,也没什么扩展性,太单调了.看了QQ2014的列表下拉刷新,发现挺好看的,我喜欢,贴一下图看一下qq的下拉刷新效果:                 

Android中ListView下拉刷新的实现方法实例分析_Android

本文实例讲述了Android中ListView下拉刷新的实现方法.分享给大家供大家参考,具体如下: ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理一下,供大家参考.那我就不解释,直接上代码了. 这里需要自己重写一下ListView,重写代码如下: package net.loonggg.listview; import java.util.Date; import android.content.Context; import android.util.