点击上方“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相关推荐

  1. uniapp实现下拉刷新及上拉(分页)加载更多(app,H5,小程序均可使用)

    开门见山地说,在移动端开发中,80%的项目都会涉及到列表展示,而有了列表不可避免的需求就是列表的下拉刷新和上拉加载更多.本篇文章主要介绍在使用uniapp开发移动端的过程中,比较好用的一个下拉及上拉组 ...

  2. Android自定义控件(四)仿网易客户端上拉加载更多

    上一篇仿得网页客户端的抽屉模式,这一篇继续,来写一写加载更多这个功能,通过自定义实现加载更多,先上图: 今天实现的就是如图中最下面的20条载入中...这个功能啦! 先来说一下思路: 1.在listvi ...

  3. 安卓设置菊花动画_Android仿ios加载loading菊花图效果

    项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loading,一般用的比较多的是仿照ios 的菊花加载loading 图,当然一些条件下还会涉及到加载成功/ 失败情况 ...

  4. AudioPlayer_听歌神器_支持同时加载10个VST效果插件

    AudioPlayer顾名思义即是一款音频播放软件,支持Flac格式的无损音频与Mp3文件,以及ogg与wav共计四种支持格式的本地音频播放器! 软件亮点:支持同时载入10款VST插件! 版权声明:在 ...

  5. js小技能:批量删除新浪微博 1、利用Chrome的console删除所有微博(支持自动加载更多,支持删除快转)2、原理:setInterval() :周期调用执行函数/表达式

    文章目录 前言 I.批量删除微博的方案 1.1 步骤 1.2 方案一:发起http请求,删除全部微博: 1.3 方案2: 利用action-type按钮事件删除微博 II.移除粉丝/关注的人 2.1 ...

  6. android listview下拉刷新动画,android 安卓 listview 支持下拉刷新 上拉加载更多

    [1]重写listViewimport java.text.SimpleDateFormat; import java.util.Date; import com.example.testdddlea ...

  7. Android下拉刷新、上拉加载更多组件FlyRefreshLayout详解

    舞动着键盘和鼠标,我誓言要把这个世界写的明明白白 本文出自门心叼龙的博客,属于原创类容,转载请注明出处.https://blog.csdn.net/geduo_83/article/details/8 ...

  8. php网站首页点击更多时获取数据,jQuery+PHP实现点击按钮加载更多,不刷新页面加载更多数据!附:可用源码+demo...

    先上效果: 刚打开页面的时候,只显示部分数据,点击加载更多的时候,就会加载我们预先定义的加载数量显示出来!当数据库里面的所有数据都显示出来,就提示全部加载了! 新建index.php jQuery+p ...

  9. Flutter ListView封装,下拉刷新、上拉加载更多

    Flutter ListView封装,下拉刷新.上拉加载更多 ​ 封装了Flutter的ListView,只要传递请求数据的方法和绘制item的方法进去就可以绘制ListView,同时支持下拉刷新.上 ...

最新文章

  1. BZOJ 2134 单选错位(数学期望)【BZOJ 修复工程】
  2. 最优控制理论 一、变分法和泛函极值问题
  3. hdu 2833(Floyd + dp)
  4. 大数据集群某节点彻底损毁后重装系统恢复(持续更新中)
  5. 255.0.0.0子网掩码相应的cidr前缀表示法是?_【洛谷日报#246】浅谈表达式的求值(Vol.2 进阶)...
  6. 洛谷P1605:迷宫(DFS)
  7. Eclipse导入包的快捷键
  8. 三星android rom开发者,三星s10刷机包安卓10(极光AuroraROM 13.0)
  9. IDEA2019版下载和安装
  10. js控制5秒后页面自动跳转
  11. MaxScript 冷门知识点
  12. 安装Image J 插件
  13. 关于LM2596S-5.0电流声问题——输出电容选择
  14. QQ号大规模被盗与你我有什么关系?你我该如何做?
  15. atan(y/x)与atan2(y,x)的区别
  16. 怎么把m2ts改成mp4
  17. 电子木鱼网页版(教学+源码带注释)
  18. 变电站智能化改造升级 数字化运营的意义
  19. BIM模型文件下载——施工场地部署模型
  20. 函数《潭州学院(心蓝)》

热门文章

  1. 命名实体识别训练集汇总(一直更新)
  2. viterbi维特比算法和隐马尔可夫模型(HMM)
  3. 用动态实现扩展TVM
  4. AI 芯片的分类及技术
  5. 2021年大数据Hadoop(九):HDFS的高级使用命令
  6. ubuntu 系统下安装 xlwt
  7. POJ 2942 Knights of the Round Table (算竞进阶习题)
  8. 常用的Percona-Toolkit工具
  9. Oracle Job定时任务的使用详解
  10. 2022-2028年中国互联网+房车行业深度调研及投资前景预测报告