RecyclerView实现上拉加载,下拉刷新

前言

  最近在使用RecyclerView,之所以不使用ListView是因为RecyclerView可以十分方便的实现横向的列表显示,这就是我抛弃ListView的原因。但是当使用纵向的RecyclerView的时候,问题就出现了。因为ListView在很多情况下是要上拉加载数据的,突然换到了RecyclerView就不知道该怎么办了。当然网上也有一些实现了屏蔽所包含View类型的上拉加载控件,但是我还是想要自己去继承RecyclerView来实现这个功能,那么就去干吧!

实现

  因为之前写过ListView的加载,那时候实现的时候是给ListView加一个FooterView,然后控制FooterView显示的时机就可以了,那么RecyclerView可不可以呢?答案是“不可以”!因为RecyclerView没有添加FooterView的方法,根本不能添加何谈控制时机啥的。那么就换个思路吧,有了!既然不能添加FooterView,那么我就把最后一个Item项变成FooterView,这样实现的效果和添加一个FooterView是一样的。思路是这样的,那么该怎么去做呢?

  很明显是要在Adapter上下功夫,在方法onCreateViewHolder中,我们返回的不是正常的ViewHolder而是我们定义的一个表示最后一个Item的ViewHolder,就叫FooterHolder吧。但是我们该怎么判断什么时候返回FooterHolder,什么时候返回正常Holder呢?

  这个时候就要用到getItemViewType(int position)方法,这个方法传入的是Item的position,我们可以做如下的判断:

        if (position + 1 == getItemCount()){
            return TYPE_FOOTER;
        } else {
            return TYPE_ITEM;
        }

  意思是说如果position+1等于总共的Item数量,那么当前Item表示的就是最后一个Item了,也就是我们要显示的加载界面。

  然后再回到onCreateViewHolder(ViewGroup parent, int viewType)方法,这里传入了viewType方法,也就是getItemViewType得到的值,我们可以利用这个值来进行判断了,如下所示:

        if(viewType==TYPE_ITEM){
            //返回正常的Holder
        }else if(viewType==TYPE_FOOTER){
            //返回FooterHolder
        }

  好了,能够将FooterHolder加入RecyclerView的最后一项了,那么接下来的就是怎样正确的将它显示出来。

  由ListView我们知道,当滑动到最后一项的时候,我们就将它显示,所以我们要对滑动进行监听,检测什么时候滑到最后一项。我们就新建一个类PTRecyclerView让它继承RecyclerView,然后在类的内部调用setOnScrollListener方法,这样就可以对滑动事件进行监听。传入的OnScrollListener我们需要覆盖其中的两个方法,如下所示:

        this.setOnScrollListener(new OnScrollListener(){
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState){
                super.onScrollStateChanged(recyclerView, newState);
                LinearLayoutManager manager = (LinearLayoutManager) getLayoutManager();
                if (newState == RecyclerView.SCROLL_STATE_IDLE
                        && lastVisibleItem + 1 == manager.getItemCount()){
                    //调用加载更多的方法
                }
            }
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy){
                super.onScrolled(recyclerView, dx, dy);
                lastVisibleItem = ((LinearLayoutManager)getLayoutManager()).findLastVisibleItemPosition();
            }
        });

  在onScrolled方法中,我用lastVisibleItem 来记录当前可见得最后一项的位置,这是为onScrollStateChanged方法服务的。在onScrollStateChanged方法中,利用lastVisibleItem 我们判断是不是可见的最后一项是RecyclerView的最后一项,如果是的话,那么就应该显示正在加载的界面,然后调用加载更多的方法。

  当然在RecyclerView内部我们我们不能直接调用加载更多的方法,而是应该用一个接口,然后在Activity中实现此接口,通过接口来实现加载更多,把加载交给Activity层来进行操作。

  这样能够实现加载更多,但是这样并不是就结束了。进行加载的话我们还需要对加载的结果进行处理,如果加载失败怎么办,如果没有更多数据了该怎么办,所以还需要后续的处理。

  我想要显示的是下面的三种情况:









  那么分别在什么时候显示呢?因为这涉及到最后一项的界面的改变,我们要拿到这个界面的对象,在PTRecyclerView内部我们是拿不到这个对象的,或者说就算拿到了也不行,我们不能把操作放在PTRecyclerView中,它只是告诉我们什么时候该加载,至于怎么加载,加载结果如何都不是它负责的事情了。所以我们要在Adapter内部来实现。

  这里我使用了别人写好的一个通用Adapter,一个RecyclerView就要对应一个Adapter,又很多的方法和逻辑又是一样的,所以就把Adapter进行封装,屏蔽了那些一样的内容,只把不一样的内容让你去实现。所以我就在这个基础上加入加载的功能,目的也是实现封装,拿过来就能使用,去除很多重复的代码。

  我们来看一下FooterHolder的部分实现:

    public FooterHolder(View itemView){
        super(itemView);
        mFooterTextView = (TextView) itemView.findViewById(R.id.tv_footer);
        mProgressBar = (ProgressBar) itemView.findViewById(R.id.footer_progressbar);
        mFooterTextView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                if(mAdapter!=null&&mAdapter.getLoadState()==BaseRecyclerViewAdapter.STATE_FAILURE){
                    mFooterTextView.setText("加载中···");
                    mProgressBar.setVisibility(View.VISIBLE);
                    mAdapter.reload();
                    mAdapter.setLoadState(BaseRecyclerViewAdapter.STATE_LOADING);
                }
            }
        });
    }

  从图中我们可以看到加载界面有一个TextView和一个ProgressBar,在构造方法中,我对TextView设置了点击监听,判断如果当前的加载状态是加载失败的话,那么点击之后就要重新进行加载。

  在Activity中,我们实现那个加载的接口中的方法loadMore:

    @Override
    public void loadMore(){
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                int result = new Random().nextInt(3);
                if(result==1){
                    //加载成功,处理数据
                    for(int i='A';i<='Z';i++)
                        mDatas.add((char)i+"");
                    mAdapter.notifyDataSetChanged();
                    mAdapter.finishLoad(BaseRecyclerViewAdapter.STATE_SUCCESS);
                }else if(result==0){
                    //加载失败
                    mAdapter.finishLoad(BaseRecyclerViewAdapter.STATE_FAILURE);
                }else if(result==2){
                    //没有更多数据了
                    mAdapter.finishLoad(BaseRecyclerViewAdapter.STATE_FINISH);
                }
            }
        },3000);
    }

  这里我就简单模拟了一下加载的结果,加载成功就把数据加到列表中,然后调用notifyDataSetChanged方法,还有加载失败和没有更多数据的处理。

  当然PTRecyclerView这个类可以选择要不要实现上拉加载的功能,这样一个类就可以实现两个功能了。我提供了一个方法,由Adapter实现。

    private boolean mCanLoadMore = false;   //说明是否实现上拉加载功能
    public void setCanLoadMore(boolean loadMore){
        mCanLoadMore = loadMore;
    }

  最后我们来看一下效果:



源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值