SwipeRefreshLayout的下拉刷新、上拉加载

SwipeRefreshLayout的下拉刷新、上拉加载


前言

  • 这次在小项目中用到了下拉刷新、上拉加载,这次就记录一下
  • 博主小白一枚,正在努力进阶,如有错误,欢迎指正!

上拉加载

  • 以下操作是在 Adapter 中,实现 Adapter 就不多说了,如果还不熟悉,建议先熟悉再做这个

1. 明确子项 Item 的类型,定义几个 int 类型的常量作为 Item 类型

  • 作用:作为不同类型的 Item 的标识符,通过判断来加载对应的布局
  • 使用在两个地方
    • 你的 Adapter 中的 onCreateViewHolder() 方法中
    • 重写的 getItemType() 方法中
    private static final int TYPE_BANNER = 0; //第一个Banner布局
    private static final int TYPE_FUNCTION = 1; //第二个功能表布局
    private static final int TYPE_SELLERS = 2; //第三个热销榜布局
    private static final int TYPE_FRUIT = 3; //第四个水果Item布局
    private static final int TYPE_FOOTER = 4; //第五个底部加载布局

2. 重写 getItemViewType() 方法

// 根据你想要的放置的 item 位置来返回不同的类型
    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return TYPE_BANNER;
        }else if (position == 1) {
            return TYPE_FUNCTION;
        }else if (position == 2) {
            return TYPE_SELLERS;
        }else if (position + 1 == getItemCount()) {
            return TYPE_FOOTER;
        }else {
            return TYPE_FRUIT;
        }
    }

3. 根据 Item 的类型,来定义对应的 ViewHolder

  • 如果只是要展示出来就不需要进行 实例的获取了
  • 如果后续操作需要实例,那么就在 ViewHolder 的构造方法中获取
// 后续操作需要实例的
    class BannerViewHolder extends RecyclerView.ViewHolder {

        ViewPager viewPager;
        LinearLayout linearLayout;

        public BannerViewHolder(@NonNull View itemView) {
            super(itemView);
            viewPager = itemView.findViewById(R.id.home_view_pager);
            linearLayout = itemView.findViewById(R.id.home_ll_indicator);
        }
    }
    
//后续操作不需要实例的
    class FunctionViewHolder extends RecyclerView.ViewHolder {

        public FunctionViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

4. 在 onCreateViewHolder() 方法中进行判断 item 类型,来加载不同的布局

  • 注意,上拉加载
  • 方法中的第二个参数 i 就是 getItemViewType() 方法中返回的
    • 这里是我的猜测,但我想应该也没有多大的偏差吧
// 根据不同的类型,加载对应的 View,并返回对应的 ViewHolder

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {

        if (i == TYPE_BANNER) {
            View view = LayoutInflater.from(context).inflate(R.layout.fragment_home_banner,
                    viewGroup,false);
            return new BannerViewHolder(view);
        }else if (i == TYPE_FUNCTION) {
            View view = LayoutInflater.from(context).inflate(R.layout.fragment_home_function
                    viewGroup,false);
            return new FunctionViewHolder(view);
        }
        
        ···
        
        }else if (i == TYPE_FOOTER) {
            View view = LayoutInflater.from(context).inflate(R.layout.fragment_home_footer,
                    viewGroup,false);
            return new FooterViewHolder(view);
        }
        
        //当 else if 语句的条件都不满足的时候,该方法就无返回值了,所以这里加一个返回值
        //不过应该是不会用到的
        return null;
    }

5. 定义滑动变量、滑动状态常量,并提供相应的 set 方法

//滑动状态常量
public final int STATE_LOADING = 0;
public final int STATE_FINISH  = 1;

//滑动状态变量
private int state = STATE_LOADING;

//相应的改变滑动状态的 set方法
public void setLoadState(int state) {
    this.state = state;
}

6. 在 onBindViewHolder() 方法中根据不同的 ViewHolder 来对相应的布局实例进行操作

  • 这里注意一点,我们在使用 RecyclerView 的时候,对于布局的具体加载,也就是对布局中的实例进行的一系列操作,应该放在 onBindViewHolder() 方法中

其实对于上拉加载这个功能来说,这一步就是上拉加载 UI 的具体展示

  • 在下面的代码在匹配到 FooterViewHolder 中进行了判断当前的状态,这样给用户 UI 呈现
    • 根据当前的 滑动状态,对应进行不同的布局操作
 @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
        
        // 此处省略部分代码,下面的是重点
        ···
        
        }else if (viewHolder instanceof FooterViewHolder) {
        //根据当前的滑动状态,对应的是实现 进度条的显示、隐藏等
            FooterViewHolder footerViewHolder = (FooterViewHolder)viewHolder;
            switch (state) {
                case STATE_LOADING:
                    footerViewHolder.progressBar.setVisibility(View.VISIBLE);
                    footerViewHolder.textView.setText("正在加载...");
                    break;
                case STATE_FINISH:
                    footerViewHolder.progressBar.setVisibility(View.GONE);
                    footerViewHolder.textView.setText("我也是有底线的哦~");
                default:
                    break;
            }

        }
    }

  • 好啦,Adapter 的工作已经作完啦,接下来就是活动、碎片的工作吧!

7. 关于加载的数据

  • 我认为,上拉加载最主要的就是 分页思想
    • 加载加载大量的数据时,不需要一次全部加载完毕,而是首先加载用户可见的部分(及手机屏幕)
    • 而在用户进行上拉的时候,在加载后面的数据
  • 分页思想就需要我们将数据处理好了

需要进行处理的内容

  • 控制数据的加载
    • 我想的就是没加载一次就请求一部分数据,然后放入一个集合中,就可以进行加载了,当全部的数据都请求完毕的时候,就不再请求,而是要通知 UI 进行更新提示数据一加载完毕了
//下面使用的是本地的数据,也控制了只能加载两次,超过两次将不再进行加载
private void getData() {
    if (loadCount <= 2){
        for (int i = 0; i < 2; i++) {
            urlList.add(String.valueOf(i));
        }
        Log.d("TAG","getData执行");
        //加载次数加一
        loadCount++;
    }
}

8. 最后一步,对 RecyclerView 的滑动监听进行处理

  • RecyclerView 的 addOnScrollListener() 方法用于给 RecyclerView 添加一个滑动监听器,也可以实现 RecyclerView.OnScrollListener 这个接口来添加监听
  • 添加监听会重写 onScrollStateChanged()onScrolled() 这两个方法,前者在滑动状态改变的时候会被调用,后者在滑动完成后会被调用
  • onScrollStateChanged() 方法的滑动状态有三个状态:
    • RecyclerView.SCROLL_STATE_IDLE:屏幕停止滚动
    • RecyclerView.SCROLL_STATE_DRAGGING:屏幕在滚动
    • RecyclerView.SCROLL_STATE_SETTLING:屏幕自动滚动,手指放开了
  • 判断是否是当前子项的最后一个,即是否需要进行加载数据
    • 总 item 数量 - 1 == 最后一个 Item 的位置
    • 总 item 数量是通过 getItemCount()得到的
  • 上面的判断成立就开始请求数据,请求完毕后,调用 notifyDataSetChanged() 方法通知 Adapter 更新数据
  • 如果多次请求之后已经请求完毕了,这时,就调用我们在 Adapter 中定义的改变滑动状态的方法了,设置已经加载完毕的状态,这个时候,因为我们在 onBindViewHolder() 中进行了判断的,所以底部的布局就会改变啦
  • 注意下面使用了 Hanlder 发送了一个延迟消息,目的就是让底部布局的加载状态能够保留一段时间,这样 UI 效果会好一些
//使用的是本地数据
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {

            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            //下面的manger是RecyclerView的主布局线性布局的实例,通过new出来的,需要传入几个参数
                int lastItemPosition = manager.findLastCompletelyVisibleItemPosition();
                int itemCount = manager.getItemCount();
                if (itemCount - 1 == lastItemPosition) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Log.d("TAG","onSrcolled()方法执行");
                            getData();
                            if (loadCount <= 2) {
                                adapter.setLoadState(adapter.STATE_LOADING);
                            }else {
                                adapter.setLoadState(adapter.STATE_FINISH);
                            }
                            adapter.notifyDataSetChanged();
                        }
                    },1000);
                }
            }
        }
 
        @Override
        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView,dx,dy);
        }
    });

下拉刷新

  • 这个比较简单,使用的是 谷歌官方的 SwipeRefreshLayout,是design库下的
  • 注意一点:.setRefreshing(); 方法并不会调用 onRefresh() 方法
  • 关于自动刷新,可以使用 Hanlder 发送一条空的延迟消息,然后进行操作,注意上面所说的
//这是项目中的代码,不要这么死板,看懂就行啦
    private void initSwipeRefresh() {

        refreshLayout = viewHome.findViewById(R.id.home_swipe_refresh);
        
        //设置 加载的那个圈圈的颜色,最多四种,这个颜色是依次加载的
        refreshLayout.setColorSchemeResources(android.R.color.holo_blue_light,
                android.R.color.holo_red_light, android.R.color.holo_orange_light,
                android.R.color.holo_green_light);
        //设置刷新监听
        refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
            //这个方法就是下拉刷新的时候会触发的,清楚原有数据,重新获取数据,通知Adapter更新数据
                urlList.clear();
                loadCount = 0;
                getData();
                adapter.notifyDataSetChanged();
                refreshLayout.setRefreshing(false);
            }
        });
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容