SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新

本文出自:http://blog.csdn.net/dt235201314/article/details/75305222

一丶效果图(代码链接见文末)

图一:1.0版(埋坑版)

SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新   

图二:2.0版(改进版)

SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新



二丶概述

RecyclerVIew的上拉加载,下拉刷新网上也有很多,包括github上的开源也有非常好用的,前面写好了RecyclerView实现首页,这里整理demo作为充实首页相关类容的开始


三丶功能介绍

1.完成上拉加载下来刷新功能

2.结合后台文档,开发情景讲解

3.踩坑记录,及问题分析,理解为什么这么做


四丶思路分析

先看接口文档:

SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新

分析:后台网络请求数据的参数是current,第一次请求current = 1,下拉刷新后请求current = current +1,当前页current< pages(总页数)才刷新

需求解决思路:

1.下来刷新用项目里常用的SwipeRefreshLayout,前面在RecyclerVIew实现主业有介绍,在下拉刷新后,重置网络请求参数为初始参数(=1)就OK。

2.上拉加载,前面学了RecyclerView多布局,设置一个“尾”布局,当current < pages时显示,提醒刷新,用RecyclerView.OnScrollListener监听,

layoutManager.findLastCompletelyVisibleItemPosition()获取最后的位置,当滑到最后时限数据请求....


五丶代码讲解

1.一个实体类解决数据问题

第一次加载20条数据,第二次加载10条

/**
 * <pre>
 *     author : JinBiao
 *     CSDN : http://my.csdn.net/DT235201314
 *     time   : 2017/07/07
 *     desc   : RecyclerView数据填充类
 *     version: 1.0
 * </pre>
 */

public class RecyclerEntity extends AbsBaseEntity {

    private String category; //品类
    private int pages; //页数
    private int sales; //销量
    private int allPage = 5;

    @Override
    public void parseData() {
    }
    
    public List<RecyclerEntity> parseData(int pages , String category ) {
        List<RecyclerEntity>  list = new ArrayList<>();
        if(pages == 1){
            for(int i= 0;i<=19;i++){
                RecyclerEntity recyclerEntity = new RecyclerEntity();
                Random r = new Random();
                recyclerEntity.setCategory(category);
                recyclerEntity.setPages(pages);
                recyclerEntity.setSales(r.nextInt(100));
                list.add(recyclerEntity);
            }
        }else {
            for(int i= 0;i<=9;i++){
                RecyclerEntity recyclerEntity = new RecyclerEntity();
                Random r = new Random();
                recyclerEntity.setCategory(category);
                recyclerEntity.setPages(pages);
                recyclerEntity.setSales(r.nextInt(100));
                list.add(recyclerEntity);
            }
        }
        return list;
    }

    @Override
    public String toString() {
        return "RecyclerEntity{" +
                "category='" + category + '\'' +
                ", pages=" + pages +
                ", sales=" + sales +
                '}';
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public int getSales() {
        return sales;
    }

    public void setSales(int sales) {
        this.sales = sales;
    }

    public int getAllPage() {
        return allPage;
    }

    public void setAllPage(int allPage) {
        this.allPage = allPage;
    }

}

2.adapter(埋坑版的)

/**
 * <pre>
 *     author : JinBiao
 *     CSDN : http://my.csdn.net/DT235201314
 *     time   : 2017/07/07
 *     desc   : 报数页面下拉加载更多Adapter
 *     version: 1.0
 * </pre>
 */

public class UpDownRefreshAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int TYPE_ITEM = 0;
    private static final int TYPE_FOOTER = 1;
    private FirstFragment firstFragment;
    private List<RecyclerEntity> records;
    boolean isNeedSetBgColor = true;
    boolean isGone = true ;
    private DecimalFormat mFormat;

    /**
     * 构造方法,把用到的值带过来,数据,可控制尾部加载提醒是否显示
     * @param firstFragment
     * @param records
     * @param isGone
     */
    public UpDownRefreshAdapter(FirstFragment firstFragment, List<RecyclerEntity> records,boolean isGone ) {
        this.firstFragment = firstFragment;
        this.records = records;
        this.isGone = isGone;
        mFormat = new DecimalFormat("#,###.##");
    }

    public interface OnItemClickListener {
        void onItemClick(View view, int position);
        void onItemLongClick(View view, int position);
    }

    private OnItemClickListener onItemClickListener;

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    @Override
    public int getItemCount() {
        if (records != null){
            return records.size() == 0 ? 0 : records.size() + 1;
        }else {
            return 0;
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (position + 1 == getItemCount()) {
            return TYPE_FOOTER;
        } else {
            return TYPE_ITEM;
        }
    }

    /**
     * 绑定数据显示栏和尾部提醒加载栏的布局
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_ITEM) {
            View view = LayoutInflater.from(firstFragment.getContext()).inflate(R.layout.up_down_refresh_data_layout, parent,
                    false);
            return new ItemViewHolder(view);
        } else if (viewType == TYPE_FOOTER) {
            View view = LayoutInflater.from(firstFragment.getContext()).inflate(R.layout.item_foot, parent,
                    false);
            return new FootViewHolder(view);
        }
        return null;
    }

    /**
     * 设置显示数据处理,控制布局背景等,底部提醒是否显示
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {

        if (holder instanceof ItemViewHolder) {
            if (records != null) {
                RecyclerEntity recyclerEntity = records.get(position);
                ((ItemViewHolder) holder).tv_category.setText(recyclerEntity.getCategory());
                ((ItemViewHolder) holder).tv_page.setText(mFormat.format(Double.valueOf(recyclerEntity.getPages())));
                ((ItemViewHolder) holder).tv_sales.setText(mFormat.format(Double.valueOf(recyclerEntity.getSales())));
                if (isNeedSetBgColor == false) {
                    ((ItemViewHolder) holder).ll_recyclerData_item.setBackgroundColor(Color.parseColor("#ffffff"));
                } else {
                    ((ItemViewHolder) holder).ll_recyclerData_item.setBackgroundColor(Color.parseColor("#fafafa"));
                }
                isNeedSetBgColor = !isNeedSetBgColor;
            }

            if (onItemClickListener != null) {
                holder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int position = holder.getLayoutPosition();
                        onItemClickListener.onItemClick(holder.itemView, position);
                    }
                });

                holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        int position = holder.getLayoutPosition();
                        onItemClickListener.onItemLongClick(holder.itemView, position);
                        return false;
                    }
                });
            }
        }

        if (holder instanceof FootViewHolder) {
            if (isGone) {
                ((FootViewHolder) holder).ll_item_loading.setVisibility(View.GONE);
            }
        }
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {

        public TextView tv_category,tv_page,tv_sales;
        public LinearLayout ll_recyclerData_item;
        public ItemViewHolder(View view) {
            super(view);
            tv_category = (TextView) view.findViewById(R.id.tv_category);
            tv_page = (TextView) view.findViewById(R.id.tv_page);
            tv_sales = (TextView) view.findViewById(R.id.tv_sales);
            ll_recyclerData_item = (LinearLayout) view.findViewById(R.id.ll_recyclerData_item);
        }
    }

    static class FootViewHolder extends RecyclerView.ViewHolder {
        public LinearLayout ll_item_loading;
        public FootViewHolder(View view) {
            super(view);
            ll_item_loading = (LinearLayout) view.findViewById(R.id.ll_item_loading);
        }
    }
}

3.fragment(埋坑版)

第一次加载显示20条,后面显示第10条(重新请求,坑1),下拉刷新重置。

public void getEntity() {
    if(isFirst){
        current = 1;
        records = recyclerEntity.parseData(current,"全部");
    }else{
        List list = new ArrayList();
        list.addAll(records);
        list.addAll(recyclerEntity.parseData(current,"全部"));
        records = list;
        }
    isLoading = (Integer.valueOf(pages) > Integer.valueOf(current));
    upDownRefreshAdapter= new UpDownRefreshAdapter(this,records,!isLoading);
    recyclerView.setAdapter(upDownRefreshAdapter);
    addScrollListener();
    mSwipeRefreshLayout.setRefreshing(false);
}

@Override
public void onRefresh() {
    isFirst = true;
    getEntity();
    mSwipeRefreshLayout.setRefreshing(false);
}

上面isLoading是用来判断是否加载,isFirst判断是否第一次加载,addScrollListener是滑动监听

private void addScrollListener () {
    if (mOnScrollListener != null) {
        recyclerView.removeOnScrollListener(mOnScrollListener);
    }
    mOnScrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) recyclerView.getLayoutManager()) {
        @Override
        public void onLoadMore(int currentPage) {
            isFirst = false;
            if (isLoading) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        current = current + 1 ;
                        getEntity();
                    }
                }, 500);
            }
        }
    };
    recyclerView.addOnScrollListener(mOnScrollListener);
}
这里监听加载的话就个重新获取下一页数据,mOnScollListener是专门的滑动监听继承类

layoutManager.findLastCompletelyVisibleItemPosition()获取最后的位置,当显示倒数第二条数据就加载,这里-1,也是可以的,-2就是提前做加载

public abstract class EndlessRecyclerOnScrollListener extends
        RecyclerView.OnScrollListener {
    public static String TAG = EndlessRecyclerOnScrollListener.class
            .getSimpleName();

    private int previousTotal = 0;
    private boolean loading = true;
    int lastCompletelyVisiableItemPosition, visibleItemCount, totalItemCount;

    private int currentPage = 1;

    private LinearLayoutManager mLinearLayoutManager;

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

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        visibleItemCount = recyclerView.getChildCount();
        totalItemCount = recyclerView.getLayoutManager().getItemCount();
        LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        lastCompletelyVisiableItemPosition =layoutManager.findLastCompletelyVisibleItemPosition();

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading
                && (visibleItemCount > 0)
                && (lastCompletelyVisiableItemPosition >= totalItemCount - 2)) {
            currentPage++;
            onLoadMore(currentPage);
            loading = true;
        }
    }

    public abstract void onLoadMore(int currentPage);
}

到这里,OK,埋坑版的就说到这里

测试发现,每次刷新后数据都会重新回到顶部。。。

。。。

错误分析:

原来,每次加载数据都是重新new的adapter,不回才怪,大神提醒要使用upDownRefreshAdapter.notifyDataSetChanged()提醒改变数据;

。。。

改完发现没效果,这是怎么回事,,看看notifyDataSetChanged()

原来new Adapter的时候,传入了数据对象的地址,数据变化,但对象的地址不会变,而你调用notifyDataChanged方法,重新调用了getSize、getView方法,虽然你没有告诉它数据变了,但是它存储的是对象地址,根据地址获取到这个对象,所以重绘页面的时候是变化后的数据

records,压根就没有初始化,从来就是复制,所以值一直没变化,初始化果然就OK了

如还有疑问参考如下文章

java将一个List赋值给另一个List相关问题


改完后,又有了第二个问题,控制尾部刷新提醒的又没有效果,再看notifyDataChanged方法

SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新

只关注数据的变化,也就是不管另一个参数的变化。

OK了,自己set咯(任何数据的变化最后都会走到set方法)


填坑版fragment

/**
 *  SwipeRefreshLayout+RecyclerView实现下拉刷新上拉自动加载
 */
public class FirstFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
    /**
     * 下拉刷新view
     */
    private SwipeRefreshLayout mSwipeRefreshLayout;
    private RecyclerView recyclerView;
    private View view;
    boolean isLoading = true;
    /**
     * 当前页
     */
    private int current;
    /**
     * 总页数
     */
    private int pages = 4;
    private List<RecyclerEntity> records = new ArrayList<>();
    boolean isFirst = true;
    private UpDownRefreshAdapter upDownRefreshAdapter;
    private RecyclerEntity recyclerEntity = new RecyclerEntity();
    private EndlessRecyclerOnScrollListener mOnScrollListener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.first_fragment, container, false);
        intView();
        return view;
    }

    public void intView() {

        mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout);
        recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
        final LinearLayoutManager layoutManager = new LinearLayoutManager(this.getActivity());
        recyclerView.setLayoutManager(layoutManager);
        getEntity();
        mSwipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary,
                R.color.blueLight,
                R.color.btn_cm_bg_pressed,
                R.color.withe);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        addScrollListener();
    }
    


    private void addScrollListener () {
        if (mOnScrollListener != null) {
            recyclerView.removeOnScrollListener(mOnScrollListener);
        }
        mOnScrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) recyclerView.getLayoutManager()) {
            @Override
            public void onLoadMore(int currentPage) {
                isFirst = false;
                if (isLoading) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            current = current + 1 ;
                            getEntity();
                        }
                    }, 500);

                }
            }
        };
        recyclerView.addOnScrollListener(mOnScrollListener);
    }

    public void getEntity() {
        if(isFirst){
            current = 1;
            records.clear();
            records.addAll( recyclerEntity.parseData(current,"全部"));
            upDownRefreshAdapter= new UpDownRefreshAdapter(this,records);
            recyclerView.setAdapter(upDownRefreshAdapter);
        }else{
            records.addAll(recyclerEntity.parseData(current,"全部"));
            upDownRefreshAdapter.notifyDataSetChanged();
            }
        isLoading = (Integer.valueOf(pages) > Integer.valueOf(current));
        upDownRefreshAdapter.setGone(!isLoading);
        addScrollListener();
        mSwipeRefreshLayout.setRefreshing(false);
    }

    @Override
    public void onRefresh() {
        isFirst = true;
        getEntity();
        mSwipeRefreshLayout.setRefreshing(false);
    }
}
填坑版adapter也就添加了一个set方法来获取是否显示尾部的提醒


SwipeRefreshLayout + RecyclerView 实现上拉加载下拉刷新就分享到这里。

如果对你有帮助,点赞关注一下博主star我的github都是对我最大的支持

源码下载欢迎Star(updating):https://github.com/JinBoy23520/CoderToDeveloperByTCLer