RecyclerView实现下拉刷新和上拉加载更多

  为了优化用户体验,从App的性能和节省用户流量方面考虑,都会涉及到对列表数据进行分页加载,使用ListView时大多都是通过自定义ListView增加头部和底部来实现下拉刷新和上拉加载更多功能,使用RecyclerView也需要处理一些滑动事件才能达到分页的效果,下面是看了一些网上的开源代码后,自己实现的一个可下拉刷新和上拉加载的RecyclerView;

package com.lcp.arecyclerview.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import com.lcp.arecyclerview.R;
import java.util.Arrays;
/** * Created by Aislli on 2015/12/29. */
public class ARecycleView extends FrameLayout {
    private int mPadding;
    private int mPaddingTop;
    private int mPaddingBottom;
    private int mPaddingLeft;
    private int mPaddingRight;
    private boolean mClipToPadding;
    private int mEmptyId;
    private RecyclerView mRecyclerView;
    private SwipeRefreshLayout mSwipeRefreshLayout;
    private ViewStub mEmpty;
    private View mEmptyView;
    private ARecyclerBaseAdapter mAdapter;
    private boolean isLoadingMore;
    private OnLoadMoreListener onLoadMoreListener;
    private RecyclerView.LayoutManager mLayoutManager;
    /** * 数据总条数 */
    private int mTotal;
    private int mMoreProgressId;
    private ViewStub mMoreProgress;
    private View mMoreProgressView;
    /** * 是否可以显示加载更多 */
    private boolean canShowLoadmore;
    int itemCount = 0;
    public ARecycleView(Context context) {
        super(context);
        initViews();
    }
    public ARecycleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttrs(attrs);
        initViews();
    }
    public ARecycleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(attrs);
        initViews();
    }
    protected void initAttrs(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.AislliRecyclerview);
        try {
            mPadding = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_Padding, -1.1f);
            mPaddingTop = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingTop, 0.0f);
            mPaddingBottom = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingBottom, 0.0f);
            mPaddingLeft = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingLeft, 0.0f);
            mPaddingRight = (int) typedArray.getDimension(R.styleable.AislliRecyclerview_PaddingRight, 0.0f);
            mClipToPadding = typedArray.getBoolean(R.styleable.AislliRecyclerview_ClipToPadding, false);
            mEmptyId = typedArray.getResourceId(R.styleable.AislliRecyclerview_EmptyView, 0);
            mMoreProgressId = typedArray.getResourceId(R.styleable.AislliRecyclerview_layout_moreProgress, R.layout.view_more_progress);
        } finally {
            typedArray.recycle();
        }
    }
    protected void initViews() {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.layout_recyclerview, this);
        mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_list);
        mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swiperefresh_layout);
        mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);
        mSwipeRefreshLayout.setEnabled(false);
        mSwipeRefreshLayout.setProgressViewOffset(false, 0, (int) TypedValue
                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources()
                        .getDisplayMetrics()));
        if (mRecyclerView != null) {
            mRecyclerView.setClipToPadding(mClipToPadding);
            if (mPadding > 0) {
                mRecyclerView.setPadding(mPadding, mPadding, mPadding, mPadding);
            } else {
                mRecyclerView.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
            }
        }
        setScrollListener();
        mMoreProgress = (ViewStub) view.findViewById(R.id.more_progress);
        mMoreProgress.setLayoutResource(mMoreProgressId);
        if (mMoreProgressId != 0)
            mMoreProgressView = mMoreProgress.inflate();
        mMoreProgress.setVisibility(View.GONE);
        mEmpty = (ViewStub) view.findViewById(R.id.emptyview);
        mEmpty.setLayoutResource(mEmptyId);
        if (mEmptyId != 0)
            mEmptyView = mEmpty.inflate();
        mEmpty.setVisibility(View.GONE);
    }
    private void setScrollListener() {
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (null == onLoadMoreListener || isLoadingMore) {
                    return;
                }
                int currentCount = 0;
                if (mLayoutManager instanceof LinearLayoutManager) {
                    currentCount = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition() + 1;
                } else if (mLayoutManager instanceof StaggeredGridLayoutManager) {
                    int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
                    Arrays.sort(lastVisibleItemPositions);
                    currentCount = lastVisibleItemPositions[lastVisibleItemPositions.length - 1] + 1;
                }
                itemCount = mAdapter.getItemCount();
                if (canShowLoadmore && newState == RecyclerView.SCROLL_STATE_IDLE
                        && currentCount == itemCount) {
                    if (mTotal > 0 && currentCount < mTotal) {
                        isLoadingMore = true;
                        mMoreProgressView.setVisibility(VISIBLE);
                        onLoadMoreListener.loadMore();
                    }
                }
            }
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }
        });
    }
    public void setLayoutManager(RecyclerView.LayoutManager manager) {
        mLayoutManager = manager;
        mRecyclerView.setLayoutManager(mLayoutManager);
    }
    public void setAdapter(ARecyclerBaseAdapter adapter) {
        mAdapter = adapter;
        mRecyclerView.setAdapter(mAdapter);
        if (mSwipeRefreshLayout != null)
            mSwipeRefreshLayout.setRefreshing(false);
        if (mAdapter != null)
            mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
                @Override
                public void onItemRangeChanged(int positionStart, int itemCount) {
                    super.onItemRangeChanged(positionStart, itemCount);
                    updateHelperDisplays();
                }
                @Override
                public void onItemRangeInserted(int positionStart, int itemCount) {
                    super.onItemRangeInserted(positionStart, itemCount);
                    updateHelperDisplays();
                }
                @Override
                public void onItemRangeRemoved(int positionStart, int itemCount) {
                    super.onItemRangeRemoved(positionStart, itemCount);
                    updateHelperDisplays();
                }
                @Override
                public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
                    super.onItemRangeMoved(fromPosition, toPosition, itemCount);
                    updateHelperDisplays();
                }
                @Override
                public void onChanged() {
                    super.onChanged();
                    updateHelperDisplays();
                }
            });
        if ((adapter == null || mAdapter.getItemCount() == 0) && mEmptyId != 0) {
            mEmpty.setVisibility(View.VISIBLE);
        }
    }
    private void updateHelperDisplays() {
        isLoadingMore = false;
        if (mSwipeRefreshLayout != null)
            mSwipeRefreshLayout.setRefreshing(false);
        if (mAdapter == null)
            return;
        if (mAdapter.getItemCount() == 0) {
            mEmpty.setVisibility(mEmptyId != 0 ? View.VISIBLE : View.GONE);
        } else if (mEmptyId != 0) {
            mEmpty.setVisibility(View.GONE);
        }
    }
    /** * Sets the {@link RecyclerView.ItemAnimator} that will handle animations involving changes * to the items in this RecyclerView. By default, RecyclerView instantiates and * uses an instance of {@link android.support.v7.widget.DefaultItemAnimator}. Whether item animations are enabled for the RecyclerView depends on the ItemAnimator and whether * the LayoutManager {@link android.support.v7.widget.RecyclerView.LayoutManager#supportsPredictiveItemAnimations() * supports item animations}. * * @param animator The ItemAnimator being set. If null, no animations will occur * when changes occur to the items in this RecyclerView. */
    public void setItemAnimator(RecyclerView.ItemAnimator animator) {
        mRecyclerView.setItemAnimator(animator);
    }
    public void setHasFixedSize(boolean hasFixedSize) {
        mRecyclerView.setHasFixedSize(hasFixedSize);
    }
    public void setRefreshing(boolean refreshing) {
        if (mSwipeRefreshLayout != null)
            mSwipeRefreshLayout.setRefreshing(refreshing);
        mLayoutManager.scrollToPosition(0);
    }
    public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) {
        mSwipeRefreshLayout.setEnabled(true);
        mSwipeRefreshLayout.setOnRefreshListener(listener);
    }
    /** * Set the load more listener of recyclerview * * @param onLoadMoreListener load listen */
    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
        this.onLoadMoreListener = onLoadMoreListener;
    }
    public void setTotal(int mTotal) {
        this.mTotal = mTotal;
        canShowLoadmore = true;
    }
    public void showMoreProgress() {
        mMoreProgress.setVisibility(View.VISIBLE);
    }
    public void hideMoreProgress() {
        mMoreProgress.setVisibility(View.GONE);
        canShowLoadmore = true;
        mLayoutManager.scrollToPosition(itemCount);
    }
    public void hideLoading() {
        if (mSwipeRefreshLayout != null)
            mSwipeRefreshLayout.setRefreshing(false);
        mMoreProgress.setVisibility(View.GONE);
        canShowLoadmore = true;
    }
    private static boolean isParallaxHeader = false;
    public static class CustomRelativeWrapper extends RelativeLayout {
        private int mOffset;
        public CustomRelativeWrapper(Context context) {
            super(context);
        }
        @Override
        protected void dispatchDraw(Canvas canvas) {
            if (isParallaxHeader)
                canvas.clipRect(new Rect(getLeft(), getTop(), getRight(), getBottom() + mOffset));
            super.dispatchDraw(canvas);
        }
        public void setClipY(int offset) {
            mOffset = offset;
            invalidate();
        }
    }
    /** * Add an {@link RecyclerView.ItemDecoration} to this RecyclerView. Item decorations can affect both measurement and drawing of individual item views. Item decorations are ordered. Decorations placed earlier in the list will be run/queried/drawn first for their effects on item views. Padding added to views will be nested; a padding added by an earlier decoration will mean further item decorations in the list will be asked to draw/pad within the previous decoration's given area. * * @param itemDecoration Decoration to add */
    public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration) {
        mRecyclerView.addItemDecoration(itemDecoration);
    }
    public interface OnLoadMoreListener {
        void loadMore();
    }
}

ARecycleView类的布局文件:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff">
    <ViewStub
        android:id="@+id/more_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:inflatedId="@id/more_progress"/>
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/more_progress">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:cacheColorHint="@null"
            android:scrollbars="none"/>
    </android.support.v4.widget.SwipeRefreshLayout>
    <ViewStub
        android:id="@+id/emptyview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:layout_marginTop="8dp"
        android:visibility="gone"/>
</RelativeLayout>

调用代码:

package com.lcp.arecyclerview;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;
import com.lcp.arecyclerview.adapter.RcListAdapter;
import com.lcp.arecyclerview.widget.ARecycleView;
import com.lcp.arecyclerview.widget.ARecyclerBaseAdapter;
import com.lcp.arecyclerview.widget.DividerItemDecoration;
import java.util.ArrayList;
/** * ListView and GridView * Created by Aislli on 2016/2/29. */
public class RecyclerListActivity extends Activity {
    private ARecycleView mRecyclerView;
    private ArrayList<String> list;
    private RcListAdapter mReListAdapter;
    private RecyclerView.LayoutManager linearLayoutManager;
    Handler handler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_list);
        initView();
        initData();
    }
    private void initView() {
        mRecyclerView = (ARecycleView) findViewById(R.id.recyclerview);
    }
    private void initData() {
        int flag = getIntent().getIntExtra("flag", 0);
        list = new ArrayList<>();
        for (int i = 0; i < 60; i++) {
            list.add("item:" + i);
        }
        if (flag == 0) {//线性布局,ListView效果
            linearLayoutManager = new LinearLayoutManager(this);
            mReListAdapter = new RcListAdapter(this, list);
            mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.HORIZONTAL));
        } else if (flag == 1) {//GridView效果
            linearLayoutManager = new GridLayoutManager(this, 3);
            mReListAdapter = new RcListAdapter(this, list);
        }
        mRecyclerView.setLayoutManager(linearLayoutManager);
        mRecyclerView.setAdapter(mReListAdapter);
        mRecyclerView.setTotal(list.size() + 6);//设置数据总数,加载完了就禁止掉加载更多事件
        setListener();
    }
    int moreNum = 1;
    int refreshNum = 1;
    private void setListener() {
        //下拉刷新
        mRecyclerView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mReListAdapter.insert(refreshNum++ + " Refresh things", 0);
                        mRecyclerView.setRefreshing(false);
                    }
                }, 1500);
            }
        });
        //上拉加载更多
        mRecyclerView.setOnLoadMoreListener(new ARecycleView.OnLoadMoreListener() {
            @Override
            public void loadMore() {
                handler.postDelayed(new Runnable() {
                    public void run() {
                        mReListAdapter.insert("More " + moreNum++, mReListAdapter.getItemCount());
                        mRecyclerView.hideMoreProgress();
                    }
                }, 1500);
            }
        });
        //点击事件
        mReListAdapter.setOnItemClickListener(new RcListAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(getApplicationContext(), "position:" + position, Toast.LENGTH_SHORT).show();
            }
        });
        //长按点击
        mReListAdapter.setOnItemLongClickListener(new ARecyclerBaseAdapter.OnItemLongClickListener() {
            @Override
            public void onItemLongClick(View view, int position) {
                Toast.makeText(getApplicationContext(), "long position:" + position, Toast.LENGTH_SHORT).show();
                mReListAdapter.remove(position);
            }
        });
    }
}

RecyclerView的下拉刷新是android.support.v4.widget.SwipeRefreshLayout控制实现的,滑动到底部自动加载下页内容主要在ARecyclerView类的这部分代码实现的:

private void setScrollListener() {
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (null == onLoadMoreListener || isLoadingMore) {
                    return;
                }
                int currentCount = 0;
                //判断最后一个item的position
                if (mLayoutManager instanceof LinearLayoutManager) {
                    currentCount = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition() + 1;
                } else if (mLayoutManager instanceof StaggeredGridLayoutManager) {
                    int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
                    Arrays.sort(lastVisibleItemPositions);
                    currentCount = lastVisibleItemPositions[lastVisibleItemPositions.length - 1] + 1;
                }
                itemCount = mAdapter.getItemCount();
                if (canShowLoadmore && newState == RecyclerView.SCROLL_STATE_IDLE
                        && currentCount == itemCount) {
                    if (mTotal > 0 && currentCount < mTotal) {
                        isLoadingMore = true;
                        mMoreProgressView.setVisibility(VISIBLE);
                        onLoadMoreListener.loadMore();
                    }
                }
            }
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }
        });
    }

解释下这段代码,判断条件流程:
1.加载更多的监听器是否为空,是否正在执行加载更多操作;
2.是否允许加载更多canShowLoadmore ,这个是在setTotal(int mTotal)方法里设置的,服务器返回的接口数据里,分页时一般都会返回total字段,调用ARecyclerView的setTotal方法设置进去后,加载完数据后会自动禁止掉加载更多的操作;
3.滑动状态是否为SCROLL_STATE_IDLE(停止滑动状态);
4.页面上显示出来的最后一个item的position是否等于当前适配器的总item数;
5.是否设置了数据总数量mTotal,页面上显示出来的最后一个item的position是否小于mTotal;
如果满足上面的所有条件,调用loadMore() 方法。

OK,上面这些就是实现RecyclerView的主要部分代码,建议有时间都自己自定义下RecyclerView这个类,可以增进自己对这个控件的了解,使用时更灵活,需要什么方法可以自己琢磨下往里加。

源码

你可能感兴趣的:(ListView,分页,下拉刷新,上拉加载)