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);
            }
        });
    }

你可能感兴趣的:(SwipeRefreshLayout的下拉刷新、上拉加载)