android 刷新某条数据_Android 支持刷新、加载更多、带反弹效果的RecyclerView
点击上方“Android技术杂货铺”,选择“标星”
干货文章,第一时间送达!
开篇
当前市面上很多支持刷新、加载更多RecyclerView
开源库,为何我这里还要自己再写一个?因为市面上的这些支持刷新加载更多的RecyclerView
开源库实现方式基本上都是:在Adapter
的外层在包裹一层Adapter
,这种实现方式主要有以下两个不方便。
1、在用户添加ItemDecoration
的时候,会影响到刷新头部和加载更多底部的样式。
2、在用户更新列表某条记录时,不方便找到该记录对应的position。例如notifyItemInserted(int position)
等。
效果截屏
gradle引用
implementation 'jsc.kit.adapter:adapter-component:_latestVersion'
属性
PullToRefreshRecyclerView
简析源码
public class PullToRefreshRecyclerView extends ViewGroup {}
1、初始化布局
private void initView(Context context) { inflate(context, R.layout.recycler_pull_to_refresh_recycler_view, this); recyclerView = findViewById(R.id.recycler_view);
final ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mMinimumVelocity = viewConfiguration.getScaledMinimumFlingVelocity(); mMaximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity(); scaledTouchSlop = viewConfiguration.getScaledTouchSlop(); }
private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullToRefreshRecyclerView, defStyleAttr, 0); int headerLayoutId = a.getResourceId(R.styleable.PullToRefreshRecyclerView_prvHeaderLayout, -1); int footerLayoutId = a.getResourceId(R.styleable.PullToRefreshRecyclerView_prvFooterLayout, -1);
//refresh text pullDownToRefreshText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvPullDownToRefreshText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvPullDownToRefreshText) : getResources().getString(R.string.recycler_default_pull_down_to_refresh); releaseToRefreshText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvReleaseToRefreshText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvReleaseToRefreshText) : getResources().getString(R.string.recycler_default_release_to_refresh); refreshingText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvRefreshingText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvRefreshingText) : getResources().getString(R.string.recycler_default_refreshing); refreshCompletedText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvRefreshCompletedText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvRefreshCompletedText) : getResources().getString(R.string.recycler_default_refresh_completed);
//load more text pullUpToLoadMoreText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvPullUpToLoadMoreText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvPullUpToLoadMoreText) : getResources().getString(R.string.recycler_default_pull_up_to_load_more); releaseToLoadMoreText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvReleaseToLoadMoreText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvReleaseToLoadMoreText) : getResources().getString(R.string.recycler_default_release_to_load_more); loadingMoreText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvLoadingMoreText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvLoadingMoreText) : getResources().getString(R.string.recycler_default_loading_more); loadMoreCompletedText = a.hasValue(R.styleable.PullToRefreshRecyclerView_prvLoadMoreCompletedText) ? a.getString(R.styleable.PullToRefreshRecyclerView_prvLoadMoreCompletedText) : getResources().getString(R.string.recycler_default_load_more_completed); a.recycle();
if (headerLayoutId == -1) { headerView = LayoutInflater.from(context).inflate(R.layout.recycler_default_header_view, this, false); setHeader(createDefaultHeader()); } else { headerView = LayoutInflater.from(context).inflate(headerLayoutId, this, false); } if (footerLayoutId == -1) { footerView = LayoutInflater.from(context).inflate(R.layout.recycler_default_footer_view, this, false); setFooter(createDefaultFooter()); } else { footerView = LayoutInflater.from(context).inflate(footerLayoutId, this, false); } addView(headerView, 0); addView(footerView);
setHaveMore(false); }
2、测量
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); headerHeight = headerView.getMeasuredHeight(); footerHeight = footerView.getMeasuredHeight(); }
3、排版页面元素
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { headerView.layout(0, 0 - headerView.getMeasuredHeight(), getMeasuredWidth(), 0); recyclerView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); footerView.layout(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() + footerView.getMeasuredHeight()); }
4、touch事件分发拦截。这里我们只拦截滑动事件,其他事件交由
RecyclerView
自己去处理。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (getState() == REFRESH_COMPLETED || getState() == LOAD_MORE_COMPLETED) return super.onInterceptTouchEvent(ev);
int action = ev.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: stopReboundAnim(); recyclerView.stopScroll(); lastTouchY = ev.getY(); break; case MotionEvent.ACTION_MOVE: float curTouchY = ev.getY(); float dy = curTouchY - lastTouchY; dy = dy > 0 ? dy + 0.5f : dy - 0.5f; lastTouchY = curTouchY; //如果滑动距离小于scaledTouchSlop,则把事件交给子View消耗; //否则此事件交由自己的onTouchEvent(MotionEvent event)方法消耗。 if (Math.abs((int) dy) >= scaledTouchSlop / 2) return true; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return super.onInterceptTouchEvent(ev); }
5、处理拦截到的滑动事件。VelocityTracker跟踪滑动速度。
@Override public boolean onTouchEvent(MotionEvent ev) { enSureVelocityTrackerNonNull(); int action = ev.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: trackMotionEvent(ev); lastTouchY = ev.getY(); break; case MotionEvent.ACTION_MOVE: trackMotionEvent(ev); float curTouchY = ev.getY(); float dy = curTouchY - lastTouchY; if (dy != 0) { dy = dy > 0 ? dy + 0.5f : dy - 0.5f; lastTouchY = curTouchY; executeMove((int) -dy); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: final VelocityTracker tracker = velocityTracker; tracker.computeCurrentVelocity(1000, mMaximumVelocity); int velocity = (int) tracker.getYVelocity(); recycleVelocityTracker(); executeUpOrCancelMotionEvent(velocity); break; } return true; }
6、执行滑动。
private void executeMove(int distance) { if (distance == 0) return;
int scrollY = getScrollY(); int scrolledY = 0; if (distance 0) {//向下滑动 //如果正在加载更多,我们避免加载更多底部视图被滑动至不可见。 if (!isLoadingMore() && scrollY > 0) { scrolledY = Math.max(0 - scrollY, distance); scrollBy(0, scrolledY); distance = distance - scrolledY; }
//滑动列表。 scrolledY = Math.max(0 - getRecyclerViewMaxCanPullDownDistance(), distance); if (scrolledY != 0) recyclerView.scrollBy(0, scrolledY);
//如果正在加载更多且已滑动至列表顶部,不可再向下滑动。 if (!isLoadingMore()) { distance = distance - scrolledY; distance = toScaledValue(distance); if (distance != 0) scrollBy(0, distance); } } else {//向上滑动 //如果正在刷新,我们避免刷新头部视图别滑动至不可见。 if (!isRefreshing() && scrollY 0) { scrolledY = Math.min(Math.abs(scrollY), distance); scrollBy(0, scrolledY); distance = distance - scrolledY; }
//滑动列表 scrolledY = Math.min(getRecyclerViewMaxCanPullUpDistance(), distance); if (scrolledY != 0) recyclerView.scrollBy(0, scrolledY);
//如果正在刷新且已滑动至列表底部,不可再向上滑动。 if (!isRefreshing()) { distance = distance - scrolledY; distance = toScaledValue(distance); if (distance != 0) scrollBy(0, distance); } }
if (getScrollY() 0) { if (!isRefreshEnable() || isRefreshing()) { header.onScroll(getState(), isRefreshEnable(), isRefreshing(), getScrollY(), headerHeight, getRefreshThresholdValue()); return; }
// getRefreshThresholdValue()释放执行刷新阈值 if (getScrollY() //release to refresh setState(RELEASE_TO_REFRESH); } else { //pull down to refresh setState(PULL_DOWN_TO_REFRESH); } header.onScroll(getState(), isRefreshEnable(), isRefreshing(), getScrollY(), headerHeight, getRefreshThresholdValue()); } else if (getScrollY() > 0) { if (!isLoadMoreEnable() || isLoadingMore()) { footer.onScroll(getState(), isLoadMoreEnable(), isLoadingMore(), getScrollY(), footerHeight); return; }
// getLoadMoreThresholdValue()释放执行加载更多阈值 if (getScrollY() > getLoadMoreThresholdValue()) { //release to load more setState(RELEASE_TO_LOAD_MORE); } else { //pull up to load more setState(PULL_UP_TO_LOAD_MORE); } footer.onScroll(getState(), isLoadMoreEnable(), isLoadingMore(), getScrollY(), footerHeight); } else { header.onScroll(getState(), isRefreshEnable(), isRefreshing(), getScrollY(), headerHeight, getRefreshThresholdValue()); footer.onScroll(getState(), isLoadMoreEnable(), isLoadingMore(), getScrollY(), footerHeight); } }
7、执行touch结束事件。
private void executeUpOrCancelMotionEvent(int velocity) { switch (getState()) { case REFRESHING: executeRebound(0 - headerHeight); recyclerView.fling(0, 0 - velocity); break; case LOADING_MORE: executeRebound(footerHeight); recyclerView.fling(0, 0 - velocity); break; case RELEASE_TO_REFRESH: executeRebound(0 - headerHeight); break; case RELEASE_TO_LOAD_MORE: executeRebound(isHaveMore() ? footerHeight : 0); break; default: executeRebound(0); recyclerView.fling(0, 0 - velocity); break; } }
private void executeRebound(int destinationScrollY) { int scrollYDistance = destinationScrollY - getScrollY(); int duration = Math.abs(scrollYDistance); duration = Math.max(200, duration); duration = Math.min(500, duration); if (animator == null) { animator = ObjectAnimator.ofPropertyValuesHolder(this, PropertyValuesHolder.ofInt(SCROLL_Y, getScrollY(), destinationScrollY)); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.addListener(new SimpleAnimatorListener() { @Override public void onAnimationEnd(Animator animation) { switch (getState()) { case RELEASE_TO_REFRESH: if (!isRefreshing() && onRefreshListener != null) { setState(REFRESHING); currentPage = startPage; onRefreshListener.onRefresh(getContext(), currentPage, pageSize); } break; case RELEASE_TO_LOAD_MORE: if (isHaveMore() && !isLoadingMore() && onRefreshListener != null) { setState(LOADING_MORE); currentPage++; onRefreshListener.onLoadMore(getContext(), currentPage, pageSize); } break; case REFRESH_COMPLETED: setState(INIT); lastRefreshTimeStamp = System.currentTimeMillis(); header.updateLastRefreshTime(lastRefreshTimeStamp); break; case LOAD_MORE_COMPLETED: setState(INIT); break; } } }); } else { animator.setIntValues(getScrollY(), destinationScrollY); } animator.setDuration(duration); animator.start(); }
使用示例
1、简单使用示例:
PullToRefreshRecyclerView pullToRefreshRecyclerView;
//设置分页加载的起始页序号以及每页数据数量 pullToRefreshRecyclerView.initializeParameters(1, 10); //关闭下拉刷新// pullToRefreshRecyclerView.setRefreshEnable(false); //关闭加载更多// pullToRefreshRecyclerView.setLoadMoreEnable(false); //设置下拉刷新和上拉加载更多监听 pullToRefreshRecyclerView.setOnRefreshListener(new PullToRefreshRecyclerView.OnRefreshListener() { @Override public void onRefresh(@NonNull Context context, int currentPage, int pageSize) { index = -1; loadNetData(); }
@Override public void onLoadMore(@NonNull Context context, int currentPage, int pageSize) { loadNetData(); } }); RecyclerView recyclerView = pullToRefreshRecyclerView.getRecyclerView(); recyclerView.setLayoutManager(new LinearLayoutManager(inflater.getContext())); recyclerView.addItemDecoration(new SpaceItemDecoration( CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_16), CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_2) ));
//模拟加载网络数据 private int index = -1; private Random random = new Random(); private void loadNetData(){ pullToRefreshRecyclerView.postDelayed(new Runnable() { @Override public void run() { //刷新(或加载更多)完成 pullToRefreshRecyclerView.completed(); List items = new ArrayList<>();int count = 7 + random.nextInt(12);for (int i = 0; i index ++; ClassItem item = new ClassItem(); item.setLabel("this is " + index); items.add(item); }//判定是否是第一页数据if (pullToRefreshRecyclerView.isFirstPage()) { adapter3.setData(items); } else { adapter3.addData(items); }//设置是否还有下一页数据 pullToRefreshRecyclerView.setHaveMore(items.size() >= pullToRefreshRecyclerView.getPageSize()); } }, 50 + random.nextInt(2000)); }
2、自定义下拉刷新:
2.1、设置刷新(头部)
"http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" app:prvHeaderLayout="@layout/xxx" android:id="@+id/pull_to_refresh_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
2.2、设置刷新逻辑监听
public void setHeader(@NonNull H header)
2.3、实现刷新逻辑
IHeader header = new IHeader() {
@Override public void initChildren(@NonNull View headerView) { //这里初始化下拉刷新view //也就是app:prvHeaderLayout="@layout/xxx"属性对应的布局文件 }
@Override public void updateLastRefreshTime(long lastRefreshTimeStamp) { //这里是上次刷新时间更新监听 }
@Override public void onUpdateState(int state, CharSequence txt) { //这里是监听下拉刷新的各种状态 //监听到的状态有:PULL_DOWN_TO_REFRESH、RELEASE_TO_REFRESH、REFRESHING、REFRESH_COMPLETED switch (state) { case PullToRefreshRecyclerView.REFRESHING: //正在刷新,我们可以正在这里启动正在刷新的动画
break; case PullToRefreshRecyclerView.REFRESH_COMPLETED: //刷新完成,我们可以在这里关闭正在刷新的动画以及头部复位
break; default: break; } }
@Override public void onScroll(int state, boolean refreshEnable, boolean isRefreshing, int scrollY, int headerHeight, int refreshThresholdValue) { //这里是监听下拉刷新动作 //监听到的状态有:INIT、PULL_DOWN_TO_REFRESH、RELEASE_TO_REFRESH、REFRESHING、REFRESH_COMPLETED } };
3、自定义上拉加载更多
3.1、设置加载更多(底部)
"http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" app:prvFooterLayout="@layout/xxx" android:id="@+id/pull_to_refresh_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
3.2、设置加载更多逻辑监听
public void setHeader(@NonNull H header)
3.3、实现加载更多逻辑
IFooter footer = new IFooter() {
@Override public void initChildren(@NonNull View footerView) { //这里初始化上拉加载更多view //也就是app:prvFooterLayout="@layout/xxx"属性对应的布局文件 }
@Override public void onUpdateState(@State int state, CharSequence txt) { //这里是监听上拉加载更多的各种状态 //监听到的状态有:PULL_UP_TO_LOAD_MORE、RELEASE_TO_LOAD_MORE、LOADING_MORE、LOAD_MORE_COMPLETED switch (state) { case PullToRefreshRecyclerView.LOADING_MORE: //正在加载更多,我们可以正在这里启动正在加载更多的动画
break; case PullToRefreshRecyclerView.LOAD_MORE_COMPLETED: //加载更多完成,我们可以在这里关闭正在加载更多的动画以及底部复位
break; default: break; } }
@Override public void onScroll(int state, boolean loadMoreEnable, boolean isLoadingMore, int scrollY, int footerHeight) { //这里是监听上拉加载更多动作 //监听到的状态有:INIT、PULL_UP_TO_LOAD_MORE、RELEASE_TO_LOAD_MORE、LOADING_MORE、LOAD_MORE_COMPLETED } };
使用介绍就到这里。
从0撸出这个开源库不容易,希望童鞋们在GitHub: https://github.com/JustinRoom/SimpleAdapterDemo 上给一颗星星✨支持下。谢谢!如果在使用过程中不懂(或需要改进的地方),可以在评论里给我留言
作者:JustinRoom
链接:https://www.jianshu.com/p/31603f12445f
String类相关面试题很难?不要慌,问题不大
为什么程序员总是发现不了自己的Bug?
Android 显示view的粒子爆炸/绽放效果
android 刷新某条数据_Android 支持刷新、加载更多、带反弹效果的RecyclerView相关推荐
- uniapp实现下拉刷新及上拉(分页)加载更多(app,H5,小程序均可使用)
开门见山地说,在移动端开发中,80%的项目都会涉及到列表展示,而有了列表不可避免的需求就是列表的下拉刷新和上拉加载更多.本篇文章主要介绍在使用uniapp开发移动端的过程中,比较好用的一个下拉及上拉组 ...
- Android自定义控件(四)仿网易客户端上拉加载更多
上一篇仿得网页客户端的抽屉模式,这一篇继续,来写一写加载更多这个功能,通过自定义实现加载更多,先上图: 今天实现的就是如图中最下面的20条载入中...这个功能啦! 先来说一下思路: 1.在listvi ...
- 安卓设置菊花动画_Android仿ios加载loading菊花图效果
项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loading,一般用的比较多的是仿照ios 的菊花加载loading 图,当然一些条件下还会涉及到加载成功/ 失败情况 ...
- AudioPlayer_听歌神器_支持同时加载10个VST效果插件
AudioPlayer顾名思义即是一款音频播放软件,支持Flac格式的无损音频与Mp3文件,以及ogg与wav共计四种支持格式的本地音频播放器! 软件亮点:支持同时载入10款VST插件! 版权声明:在 ...
- js小技能:批量删除新浪微博 1、利用Chrome的console删除所有微博(支持自动加载更多,支持删除快转)2、原理:setInterval() :周期调用执行函数/表达式
文章目录 前言 I.批量删除微博的方案 1.1 步骤 1.2 方案一:发起http请求,删除全部微博: 1.3 方案2: 利用action-type按钮事件删除微博 II.移除粉丝/关注的人 2.1 ...
- android listview下拉刷新动画,android 安卓 listview 支持下拉刷新 上拉加载更多
[1]重写listViewimport java.text.SimpleDateFormat; import java.util.Date; import com.example.testdddlea ...
- Android下拉刷新、上拉加载更多组件FlyRefreshLayout详解
舞动着键盘和鼠标,我誓言要把这个世界写的明明白白 本文出自门心叼龙的博客,属于原创类容,转载请注明出处.https://blog.csdn.net/geduo_83/article/details/8 ...
- php网站首页点击更多时获取数据,jQuery+PHP实现点击按钮加载更多,不刷新页面加载更多数据!附:可用源码+demo...
先上效果: 刚打开页面的时候,只显示部分数据,点击加载更多的时候,就会加载我们预先定义的加载数量显示出来!当数据库里面的所有数据都显示出来,就提示全部加载了! 新建index.php jQuery+p ...
- Flutter ListView封装,下拉刷新、上拉加载更多
Flutter ListView封装,下拉刷新.上拉加载更多 封装了Flutter的ListView,只要传递请求数据的方法和绘制item的方法进去就可以绘制ListView,同时支持下拉刷新.上 ...
最新文章
- BZOJ 2134 单选错位(数学期望)【BZOJ 修复工程】
- 最优控制理论 一、变分法和泛函极值问题
- hdu 2833(Floyd + dp)
- 大数据集群某节点彻底损毁后重装系统恢复(持续更新中)
- 255.0.0.0子网掩码相应的cidr前缀表示法是?_【洛谷日报#246】浅谈表达式的求值(Vol.2 进阶)...
- 洛谷P1605:迷宫(DFS)
- Eclipse导入包的快捷键
- 三星android rom开发者,三星s10刷机包安卓10(极光AuroraROM 13.0)
- IDEA2019版下载和安装
- js控制5秒后页面自动跳转
- MaxScript 冷门知识点
- 安装Image J 插件
- 关于LM2596S-5.0电流声问题——输出电容选择
- QQ号大规模被盗与你我有什么关系?你我该如何做?
- atan(y/x)与atan2(y,x)的区别
- 怎么把m2ts改成mp4
- 电子木鱼网页版(教学+源码带注释)
- 变电站智能化改造升级 数字化运营的意义
- BIM模型文件下载——施工场地部署模型
- 函数《潭州学院(心蓝)》