笔记20170420--用PullRefreshLayout+RecyclerView实现上拉加载下拉刷新(Android)

需求:

达到像LoadMoreListView那样的效果,实现分页时上拉加载等待时间有个footerView

要达到的效果

笔记20170420--用PullRefreshLayout+RecyclerView实现上拉加载下拉刷新(Android)_第1张图片
上拉加载.jpg
笔记20170420--用PullRefreshLayout+RecyclerView实现上拉加载下拉刷新(Android)_第2张图片
下拉刷新.jpg

PullRefreshLayout+RecyclerView相关代码:

xml--Activity的布局:



    

        
    


xml--上拉加载的footerView的布局(R.layout.load_more_footer):



    

    

xml--adapter用到的item布局



    

        

        

        

        


网上找的HeaderViewRecyclerAdapter:
package xxx.xxx.xxx.xxx.adapter;

/*
 * Copyright (C) 2014 darnmason
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 

* RecyclerView adapter designed to wrap an existing adapter allowing the addition of * header views and footer views. *

*

* I implemented it to aid with the transition from ListView to RecyclerView where the ListView's * addHeaderView and addFooterView methods were used. Using this class you may initialize your * header views in the Fragment/Activity and add them to the adapter in the same way you used to * add them to a ListView. *

*

* I also required to be able to swap out multiple adapters with different content, therefore * setAdapter may be called multiple times. *

* Created by darnmason on 07/11/2014. */ public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter { private static final int HEADERS_START = Integer.MIN_VALUE; private static final int FOOTERS_START = Integer.MIN_VALUE + 10; private static final int ITEMS_START = Integer.MIN_VALUE + 20; private static final int ADAPTER_MAX_TYPES = 100; private RecyclerView.Adapter mWrappedAdapter; private List mHeaderViews, mFooterViews; private Map mItemTypesOffset; /** * Construct a new header view recycler adapter * @param adapter The underlying adapter to wrap */ public HeaderViewRecyclerAdapter(RecyclerView.Adapter adapter) { mHeaderViews = new ArrayList(); mFooterViews = new ArrayList(); mItemTypesOffset = new HashMap(); setWrappedAdapter(adapter); } /** * Replaces the underlying adapter, notifying RecyclerView of changes * @param adapter The new adapter to wrap */ public void setAdapter(RecyclerView.Adapter adapter) { if(mWrappedAdapter != null && mWrappedAdapter.getItemCount() > 0) { notifyItemRangeRemoved(getHeaderCount(), mWrappedAdapter.getItemCount()); } setWrappedAdapter(adapter); notifyItemRangeInserted(getHeaderCount(), mWrappedAdapter.getItemCount()); } @Override public int getItemViewType(int position) { int hCount = getHeaderCount(); if (position < hCount) return HEADERS_START + position; else { int itemCount = mWrappedAdapter.getItemCount(); if (position < hCount + itemCount) { return getAdapterTypeOffset() + mWrappedAdapter.getItemViewType(position - hCount); } else return FOOTERS_START + position - hCount - itemCount; } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { if (viewType < HEADERS_START + getHeaderCount()) return new StaticViewHolder(mHeaderViews.get(viewType - HEADERS_START)); else if (viewType < FOOTERS_START + getFooterCount()) return new StaticViewHolder(mFooterViews.get(viewType - FOOTERS_START)); else { return mWrappedAdapter.onCreateViewHolder(viewGroup, viewType - getAdapterTypeOffset()); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { int hCount = getHeaderCount(); if (position >= hCount && position < hCount + mWrappedAdapter.getItemCount()) mWrappedAdapter.onBindViewHolder(viewHolder, position - hCount); } /** * Add a static view to appear at the start of the RecyclerView. Headers are displayed in the * order they were added. * @param view The header view to add */ public void addHeaderView(View view) { mHeaderViews.add(view); } /** * Add a static view to appear at the end of the RecyclerView. Footers are displayed in the * order they were added. * @param view The footer view to add */ public void addFooterView(View view) { mFooterViews.add(view); } @Override public int getItemCount() { return getHeaderCount() + getFooterCount() + getWrappedItemCount(); } /** * @return The item count in the underlying adapter */ public int getWrappedItemCount() { return mWrappedAdapter.getItemCount(); } /** * @return The number of header views added */ public int getHeaderCount() { return mHeaderViews.size(); } /** * @return The number of footer views added */ public int getFooterCount() { return mFooterViews.size(); } private void setWrappedAdapter(RecyclerView.Adapter adapter) { if (mWrappedAdapter != null) mWrappedAdapter.unregisterAdapterDataObserver(mDataObserver); mWrappedAdapter = adapter; Class adapterClass = mWrappedAdapter.getClass(); if(!mItemTypesOffset.containsKey(adapterClass)) putAdapterTypeOffset(adapterClass); mWrappedAdapter.registerAdapterDataObserver(mDataObserver); } private void putAdapterTypeOffset(Class adapterClass) { mItemTypesOffset.put(adapterClass, ITEMS_START + mItemTypesOffset.size() * ADAPTER_MAX_TYPES); } private int getAdapterTypeOffset() { return mItemTypesOffset.get(mWrappedAdapter.getClass()); } private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { super.onChanged(); notifyDataSetChanged(); } @Override public void onItemRangeChanged(int positionStart, int itemCount) { super.onItemRangeChanged(positionStart, itemCount); notifyItemRangeChanged(positionStart + getHeaderCount(), itemCount); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { super.onItemRangeInserted(positionStart, itemCount); notifyItemRangeInserted(positionStart + getHeaderCount(), itemCount); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { super.onItemRangeRemoved(positionStart, itemCount); notifyItemRangeRemoved(positionStart + getHeaderCount(), itemCount); } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { super.onItemRangeMoved(fromPosition, toPosition, itemCount); int hCount = getHeaderCount(); // TODO: No notifyItemRangeMoved method? notifyItemRangeChanged(fromPosition + hCount, toPosition + hCount + itemCount); } }; private static class StaticViewHolder extends RecyclerView.ViewHolder { public StaticViewHolder(View itemView) { super(itemView); } } //下边这个方法是我自己拷贝另一个网上的adpter的,作用是获得footerview,好在加载完的时候直接隐藏掉footerview public View getFooterView() { return getFooterCount()>0 ? mFooterViews.get(0) : null; } }
网上找的自定义onScrollListener:
package xxx.xxx.xxx.common.listener;

import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;


public abstract class SysMsgListScrollListener  extends RecyclerView.OnScrollListener{

    private LinearLayoutManager mLinearLayoutManager;
    int lastVisibleItemPosition;

    public SysMsgListScrollListener(LinearLayoutManager linearLayoutManager) {
        this.mLinearLayoutManager = linearLayoutManager;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        //获取最后一个显示 的item的位置
        lastVisibleItemPosition = mLinearLayoutManager.findLastVisibleItemPosition();
    }

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        //这一段是关键,如果显示的item总数大于0,并且滑动状态符合,并且最后一个位置大于等于item总数量-1的话(代表最后一个) 就执行加载方法onLoadMore();
        int currentScrollState = newState;
        int visibleItemCount = mLinearLayoutManager.getChildCount();
        int totalItemCount = mLinearLayoutManager.getItemCount();
        if ((visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE && (lastVisibleItemPosition) >= totalItemCount - 1)) {
            onLoadMore();
        }
    }

    public abstract void onLoadMore();

}

自己写的一个接口,用作item点击监听:
package xxx.xxx.xxx.common.listener;

/**
 * Created by Hello我的World on 2017/4/20.
 */

public interface SysMsgOnItemClickListener {
     void sysMsgItemClick(long id);
}

ViewHolder:
package xxx.xxx.xxx.xxx.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import xxx.xxx.xxx.R;
import xxx.xxx.xxx.common.bean.SysMsgInfo;
import xxx.xxx.xxx.common.listener.SysMsgOnItemClickListener;

import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

/**
 * Created by Hello我的World on 2017/4/20.
 */

public class ViewHolder extends RecyclerView.ViewHolder  {

    @BindView(R.id.msg_layout)
    LinearLayout msg_layout;
    @BindView(R.id.msg_title)
    TextView msg_title;
    @BindView(R.id.msg_time)
    TextView msg_time;
    
    SysMsgOnItemClickListener listener;
    List data;

    public ViewHolder(View itemView, SysMsgOnItemClickListener listener, Listdata) {
        super(itemView);
        ButterKnife.bind(this,itemView);
        this.listener = listener;
        this.data = data;
    }

    @OnClick(R.id.msg_layout)
    public void onClick(){
        listener.sysMsgItemClick(data.get(getLayoutPosition()).getId());
    }

    public void setContent(SysMsgInfo info){
        msg_title.setText(info.getTitle());
        msg_time.setText(info.getCreateTime());
    }

}
真正加载数据的Adapter(用来放到上边那个“网上找的adapter”里):
package xxx.xxx.xxx.common.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import xxx.xxx.xxx.R;
import xxx.xxx.xxx.common.bean.SysMsgInfo;
import xxx.xxx.xxx.common.listener.SysMsgOnItemClickListener;

import java.util.List;

/**
 * Created by Hello我的World on 2017/4/20.
 */

public class SysMsgListAdapter extends RecyclerView.Adapter {

    Context context;
    List data;//这个list里的对象,是自己写的数据databean,按自己实际需求实现
    SysMsgOnItemClickListener onItemClickListener;//这个监听器是自己写的接口,就一个方法,让item收到click事件时可以回调

    public SysMsgListAdapter(Context context,Listdata,SysMsgOnItemClickListener listener){
        this.context = context;
        this.data = data;
        this.onItemClickListener = listener;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.sysmsg_item, parent, false);
        return new ViewHolder(view,onItemClickListener,data);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.setContent(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    public List getData() {
        return data;
    }
}
使用:
package xxx.xxx.xxx.common.activity;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import com.baoyz.widget.PullRefreshLayout;
import xxx.xxx.xxx.R;
import xxx.xxx.xxx.base.BaseActivity;
import xxx.xxx.xxx.base.utils.CommonUtil;
import xxx.xxx.xxx.common.adapter.HeaderViewRecyclerAdapter;
import xxx.xxx.xxx.common.adapter.SysMsgListAdapter;
import xxx.xxx.xxx.common.bean.GetSysMessageListReturnInfo;
import xxx.xxx.xxx.common.contract.SysMsgListContract;
import xxx.xxx.xxx.common.listener.SysMsgListScrollListener;
import xxx.xxx.xxx.common.presenter.SysMsgListPresenter;

import butterknife.BindView;
import butterknife.ButterKnife;

public class SysMsgListActivity extends BaseActivity implements SysMsgListContract.View {
    @BindView(R.id.pullRefreshLayout)
    PullRefreshLayout pullRefreshLayout;
    @BindView(R.id.tv_title)
    TextView tv_title;
    @BindView(R.id.listView)
    RecyclerView listView;

    SysMsgListContract.Presenter presenter;
    HeaderViewRecyclerAdapter hAdapter;
    SysMsgListAdapter adapter;
    View footer;
    int pageIndex = 1;
    int pageSize = 20;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sys_msg_list);
        ButterKnife.bind(this);
        tv_title.setText("系统消息中心");
        //这是这个activity的presenter,loadSysMsgList方法是加载数据的方法
        presenter = new SysMsgListPresenter(this);
        presenter.loadSysMsgList(pageIndex, pageSize);
        //布局管理器
        LinearLayoutManager l = new LinearLayoutManager(this);
        listView.setLayoutManager(l);
        listView.addOnScrollListener(new SysMsgListScrollListener(l) {
            @Override
            public void onLoadMore() {
                //这个是RecyclerView的滑动监听,如果滑动到最后  就重新加载,先把footerView显示出来
                if(footer!=null) footer.setVisibility(View.VISIBLE);
                //执行加载
                presenter.loadSysMsgList(pageIndex, pageSize);
            }
        });
        pullRefreshLayout.setOnRefreshListener(() -> {
            //下拉刷新操作,直接将pageIndex还原
            pageIndex = 1;
            //如果adapter有数据,直接清掉,并且将“网上找来的adapter”更新
            if (adapter != null) {
                adapter.getData().clear();
                if (hAdapter != null) {
                    hAdapter.notifyDataSetChanged();
                }
            }
            //加载数据
            presenter.loadSysMsgList(pageIndex, pageSize);
        });
    }

    @Override
    public void showProgress(String message) {

    }

    @Override
    public void dismissProgress() {

    }
    //下边这个方法是Adapter的关键代码段
    @Override
    public void showMsgList(GetSysMessageListReturnInfo data, int pageIndex) {
        pullRefreshLayout.setRefreshing(false);//数据返回后,将下拉刷新状态设置为false
        if (data.getMessageList().size() == 0) return;//如果数据为0就不用显示了
        this.pageIndex = pageIndex;//分页加载的页码
        //如果adapter为空,就新建adapter,如果不为空代表之前有过加载,直接在adapter的数组上addAll好了
        if (adapter == null) {
            adapter = new SysMsgListAdapter(this, data.getMessageList(), id -> {
                //这个就是SysMsgOnItemClickListener的回调,然后做出自己需要的逻辑处理即可 
                Intent intent = new Intent(SysMsgListActivity.this,SysMsgDetailActivity.class);
                intent.putExtra("MsgId",id);
                startActivity(intent);
            });
        } else {
            adapter.getData().addAll(data.getMessageList());
            if (hAdapter != null) hAdapter.notifyDataSetChanged();
        }
        //如果“在网上找的Adapter”为空,就新创建一个,新建对象时将我们自己展示数据的adapter传进去
        if (hAdapter == null) {
            hAdapter = new HeaderViewRecyclerAdapter(adapter);
            listView.setAdapter(hAdapter);
            //isLastPage==0代表不是最后一页,isLastPage==1代表最后一页
            if (data.getIsLastPage() != 1) {
                //如果加载返回的数据中isLastPage表示不是最后一页,就要添加footerView
                footer = LayoutInflater.from(this).inflate(R.layout.load_more_footer, listView, false);
                hAdapter.addFooterView(footer);
            }
        }
        //数据加载完成时将footerView隐藏
        if (footer != null) footer.setVisibility(View.GONE);
    }

    @Override
    public void loadDone() {
        //加载到最后数据,当服务器返回已经没有数据了,presenter中会回调这个方法,在这里将footerView隐藏掉
        CommonUtil.showToast("数据已加载到最后...");
        if (footer != null) footer.setVisibility(View.GONE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.onDestroy();
    }


}

实现思路:

  • Activity的布局(要包含PullRefreshLayout和RecyclerView)
    这里用的是com.baoyz.widget.PullRefreshLayout,然后这个布局中包一个RecyclerView
  • 创建footerView的布局
  • 创建adapter加载数据要用到的item
  • 要有一个可以加footerView的adapter,网上找的HeaderViewRecyclerAdapter 点击这里看这个adapter的源码
  • 实现onItemClick的监听器(其实就是个接口,在item监听到点击着自己了就回调这个接口的方法)
  • 要知道RecyclerView滑动了没呀,要根据滑动状态判断加载不加载啊(onScrollListener RecyclerView滑动监听,ps.这个监听的源码地址找不到了)
  • 要有一个ViewHolder
  • 要有一个放数据的Adapter
  • Activity里的一系列使用

以上是笔记,其实去年已经写过类似需求,也找到一套方法,没有记笔记,现在用到时发现全忘了,重新找资料看的,所以这次一定要记录好备用!
BTW:方法千千万,还有很多更简洁更快的方法----->不信就去看啊
再看啊

你可能感兴趣的:(笔记20170420--用PullRefreshLayout+RecyclerView实现上拉加载下拉刷新(Android))