RecyclerView下拉刷新、上拉加載框架

Android中下拉刷新的框架可謂是多不可數,今天,我給大家介紹一下我最近新學習到的一個基於RecyclerView的一個下拉刷新框架,支持上拉加載,並且可以自定義上拉加載視圖動畫,下拉刷新是基於SwipeRefreshLayout的。項目地址奉上 https://github.com/niniloveyou/SwipeRecyclerView

運行效果

1、首先在modle的gradle中對RecyclerView進行配置

 compile 'com.android.support:recyclerview-v7:23.3.0'

2、我們這個下拉刷新框架是基於SwipeRefreshLayout+RecyclerView的所以我們將這個進行了封裝
自定義SwipeRecyclerView 類

public class SwipeRecyclerView extends FrameLayout
                implements SwipeRefreshLayout.OnRefreshListener{

    private View mEmptyView;
    private BaseFooterView mFootView;
    private RecyclerView recyclerView;
    private SwipeRefreshLayout mRefreshLayout;

    private LayoutManager mLayoutManager;
    private OnLoadListener mListener;
    private SpanSizeLookup mSpanSizeLookup;
    private DataObserver mDataObserver;
    private WrapperAdapter mWrapperAdapter;

    private boolean isEmptyViewShowing;
    private boolean isLoadingMore;
    private boolean isLoadMoreEnable;
    private boolean isRefreshEnable;

    private int lastVisiablePosition = 0;

    public SwipeRecyclerView(Context context) {
        this(context, null);
    }

    public SwipeRecyclerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwipeRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setupSwipeRecyclerView();
    }

    private void setupSwipeRecyclerView() {

        isEmptyViewShowing = false;
        isRefreshEnable = true;
        isLoadingMore = false;
        isLoadMoreEnable = true;

        mFootView = new SimpleFooterView(getContext());

        View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_swipe_recyclerview, this);
        mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.SwipeRefreshLayout);
        recyclerView = (RecyclerView) view.findViewById(R.id.RecyclerView);
        mLayoutManager = recyclerView.getLayoutManager();

        mRefreshLayout.setOnRefreshListener(this);
        recyclerView.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                // do nothing if load more is not enable or refreshing or loading more
                if(!isLoadMoreEnable || isRefreshing() || isLoadingMore){
                    return;
                }

                //get the lastVisiablePosition
                mLayoutManager = recyclerView.getLayoutManager();
                if(mLayoutManager instanceof LinearLayoutManager){
                    lastVisiablePosition = ((LinearLayoutManager)mLayoutManager).findLastVisibleItemPosition();
                }else if(mLayoutManager instanceof GridLayoutManager){
                    lastVisiablePosition = ((GridLayoutManager)mLayoutManager).findLastCompletelyVisibleItemPosition();
                }else if(mLayoutManager instanceof StaggeredGridLayoutManager){
                    int[] into = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()];
                    ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(into);
                    lastVisiablePosition = findMax(into);
                }

                int childCount = mWrapperAdapter == null ? 0 : mWrapperAdapter.getItemCount();
                Log.i("info","lastVisiablePosition="+lastVisiablePosition+",childCount-1="+(childCount-1));
                if(childCount > 1 && lastVisiablePosition == childCount - 1){

                    if(mListener != null){
                        isLoadingMore = true;
                        mListener.onLoadMore();
                    }
                }
            }
        });
    }

    private int findMax(int[] lastPositions) {
        int max = lastPositions[0];
        for (int value : lastPositions) {
            if (value > max) {
                max = value;
            }
        }
        return max;
    }

    /**
     * set is enable pull to refresh
     * @param refreshEnable
     */
    public void setRefreshEnable(boolean refreshEnable){
        isRefreshEnable = refreshEnable;
        mRefreshLayout.setEnabled(isRefreshEnable);
    }

    public boolean getRefreshEnable(){
        return isRefreshEnable;
    }

    /**
     * set is loading more enable
     * @param loadMoreEnable
     *              if true when recyclerView scroll to bottom load more action will be trigger
     */
    public void setLoadMoreEnable(boolean loadMoreEnable) {
        if(!loadMoreEnable){
            stopLoadingMore();
        }
        isLoadMoreEnable = loadMoreEnable;
    }

    public boolean getLoadMoreEnable(){
        return isLoadMoreEnable;
    }

    /**
     * get is refreshing
     * @return
     */
    public boolean isRefreshing(){
        return mRefreshLayout.isRefreshing();
    }

    /**
     * get is loading more
     * @return
     */
    public boolean isLoadingMore(){
        return isLoadingMore;
    }

    /**
     * is empty view showing
     * @return
     */
    public boolean isEmptyViewShowing(){
        return isEmptyViewShowing;
    }

    /**
     * you may need set some other attributes of swipeRefreshLayout
     * @return
     *     swipeRefreshLayout
     */
    public SwipeRefreshLayout getSwipeRefreshLayout(){
        return mRefreshLayout;
    }

    /**
     * you may need set some other attributes of RecyclerView
     * @return
     *     RecyclerView
     */
    public RecyclerView getRecyclerView(){
        return recyclerView;
    }

    /**
     * set load more listener
     * @param listener
     */
    public void setOnLoadListener(OnLoadListener listener){
        mListener = listener;
    }

    /**
     * support for GridLayoutManager
     * @param spanSizeLookup
     */
    public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup){
        this.mSpanSizeLookup = spanSizeLookup;
    }

    /**
     * set the footer view
     * @param footerView
     *        the view to be showing when pull up
     */
    public void setFooterView(BaseFooterView footerView){
        if(footerView != null) {
            this.mFootView = footerView;
        }
    }

    /**
     * set a empty view like listview
     * @param emptyView
     *        the view to be showing when the data set size is zero
     */
    public void setEmptyView(View emptyView){
        if(mEmptyView != null){
            removeView(mEmptyView);
        }
        this.mEmptyView = emptyView;

        if(mDataObserver != null) {
            mDataObserver.onChanged();
        }
    }

    /**
     * set adapter to recyclerView
     * @param adapter
     */
    public void setAdapter(RecyclerView.Adapter adapter){
        if(adapter != null) {
            if(mDataObserver == null){
                mDataObserver = new DataObserver();
            }
            mWrapperAdapter = new WrapperAdapter(adapter);
            recyclerView.setAdapter(mWrapperAdapter);
            adapter.registerAdapterDataObserver(mDataObserver);
            mDataObserver.onChanged();
        }
    }

    /**
     * refresh or load more completed
     */
    public void complete(){
        mRefreshLayout.setRefreshing(false);
        stopLoadingMore();
    }

    /**
     * set refreshing
     * if you want load data when first in, you can setRefreshing(true)
     * after {@link #setOnLoadListener(OnLoadListener)}
     * @param refreshing
     */
    public void setRefreshing(boolean refreshing){
        mRefreshLayout.setRefreshing(refreshing);
        if(refreshing && !isLoadingMore && mListener != null){
            mListener.onRefresh();
        }
    }

    /**
     * stop loading more without animation
     */
    public void stopLoadingMore(){
        isLoadingMore = false;
        if(mWrapperAdapter != null) {
            mWrapperAdapter.notifyItemRemoved(mWrapperAdapter.getItemCount());
        }
    }

    /**
     * call method {@link OnLoadListener#onRefresh()}
     */
    @Override
    public void onRefresh() {
        if(mListener != null){

            //reset footer view status loading
            if(mFootView != null){
                mFootView.onLoadingMore();
            }
            mListener.onRefresh();
        }
    }

    /**
     *
     * call when network is available or not available
     */
    public void onNetChange(boolean isAvailable) {
        if(mFootView != null){
            mFootView.onNetChange(isAvailable);
        }
    }

    /**
     * call when you need change footer view to loading status
     */
    public void onLoadingMore() {
        if(mFootView != null){
            mFootView.onLoadingMore();
        }
    }

    /**

     * call when no more data add to list
     */
    public void onNoMore(CharSequence message) {
        if(mFootView != null){
            mFootView.onNoMore(message);
        }
    }

    /**

     * call when you need show error message
     */
    public void onError(CharSequence message) {
        if(mFootView != null){
            mFootView.onError(message);
        }
    }


    private class WrapperAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

        public static final int TYPE_FOOTER = 0x100;

        RecyclerView.Adapter<RecyclerView.ViewHolder> mInnerAdapter;

        public WrapperAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter){
            this.mInnerAdapter = adapter;
        }

        public boolean isLoadMoreItem(int position){
            return isLoadMoreEnable && position == getItemCount() - 1;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if(TYPE_FOOTER == viewType){
                return new FooterViewHolder(mFootView);
            }
            return mInnerAdapter.onCreateViewHolder(parent, viewType);
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            if(isLoadMoreItem(position)){
                return;
            }
            mInnerAdapter.onBindViewHolder(holder, position);
        }


        @Override
        public int getItemViewType(int position) {
            if(isLoadMoreItem(position)){
                return TYPE_FOOTER;
            }else{
                return mInnerAdapter.getItemViewType(position);
            }
        }

        @Override
        public int getItemCount() {
            int count = mInnerAdapter == null ? 0 : mInnerAdapter.getItemCount();

            //without loadingMore when adapter size is zero
            if(count == 0){
                return 0;
            }
            return isLoadMoreEnable ? count + 1 : count;
        }

        @Override
        public long getItemId(int position) {
           return mInnerAdapter.getItemId(position);
        }

        @Override
        public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
            ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
            if (lp != null
                    && lp instanceof StaggeredGridLayoutManager.LayoutParams
                    && isLoadMoreItem(holder.getLayoutPosition()))
            {
                StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
                p.setFullSpan(true);
            }
            mInnerAdapter.onViewAttachedToWindow(holder);
        }

        @Override
        public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
            mInnerAdapter.onViewDetachedFromWindow(holder);
        }

        @Override
        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
            LayoutManager manager = recyclerView.getLayoutManager();
            if (manager instanceof GridLayoutManager) {
                final GridLayoutManager gridManager = ((GridLayoutManager) manager);
                gridManager.setSpanSizeLookup(new SpanSizeLookup() {
                    @Override
                    public int getSpanSize(int position) {
                        boolean isLoadMore = isLoadMoreItem(position);
                        if(mSpanSizeLookup != null && !isLoadMore){
                            return mSpanSizeLookup.getSpanSize(position);
                        }
                        return isLoadMore ? gridManager.getSpanCount() : 1;
                    }
                });
            }
            mInnerAdapter.onAttachedToRecyclerView(recyclerView);
        }

        @Override
        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
            mInnerAdapter.onDetachedFromRecyclerView(recyclerView);
        }

        @Override
        public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
            return mInnerAdapter.onFailedToRecycleView(holder);
        }

        @Override
        public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
            mInnerAdapter.registerAdapterDataObserver(observer);
        }

        @Override
        public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
            mInnerAdapter.unregisterAdapterDataObserver(observer);
        }

        @Override
        public void onViewRecycled(RecyclerView.ViewHolder holder) {
            mInnerAdapter.onViewRecycled(holder);
        }
    }

    /**
     * ViewHolder of footerView
     */
    private class FooterViewHolder extends RecyclerView.ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
        }
    }

    /**
     * a inner class used to monitor the dataSet change
     * <p>
     * because wrapperAdapter do not know when wrapperAdapter.mInnerAdapter
     * <p>
     * dataSet changed, these method are final
     */
    class DataObserver extends RecyclerView.AdapterDataObserver{

        @Override
        public void onChanged() {
            super.onChanged();
            RecyclerView.Adapter adapter = recyclerView.getAdapter();
            if(adapter != null && mEmptyView != null){

                int count = 0;
                if(isLoadMoreEnable && adapter.getItemCount() != 0){
                    count ++;
                }
                if(adapter.getItemCount() == count){
                    isEmptyViewShowing = true;
                    if(mEmptyView.getParent() == null){
                        LayoutParams params = new LayoutParams(
                                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                        params.gravity = Gravity.CENTER;

                        addView(mEmptyView, params);
                    }

                    recyclerView.setVisibility(GONE);
                    mEmptyView.setVisibility(VISIBLE);
                }else{
                    isEmptyViewShowing = false;
                    mEmptyView.setVisibility(GONE);
                    recyclerView.setVisibility(VISIBLE);
                }
            }
            mWrapperAdapter.notifyDataSetChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            super.onItemRangeChanged(positionStart, itemCount);
            mWrapperAdapter.notifyItemRangeChanged(positionStart, itemCount);
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            super.onItemRangeChanged(positionStart, itemCount, payload);
            mWrapperAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            super.onItemRangeInserted(positionStart, itemCount);
            mWrapperAdapter.notifyItemRangeInserted(positionStart, itemCount);
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount);
           mWrapperAdapter.notifyItemRangeRemoved(fromPosition, itemCount);
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            super.onItemRangeRemoved(positionStart, itemCount);
            mWrapperAdapter.notifyItemRangeRemoved(positionStart, itemCount);
        }

    }

    public interface OnLoadListener {

        void onRefresh();

        void onLoadMore();
    }
}

3、SwipeRecyclerView控件的佈局layout_swipe_recyclerview.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/SwipeRefreshLayout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/RecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:cacheColorHint="@null"
        android:scrollbars="none"/>

</android.support.v4.widget.SwipeRefreshLayout>

4、BaseFootView.class

public abstract class BaseFooterView extends FrameLayout implements FooterViewListener{

    public BaseFooterView(Context context) {
        super(context);
    }

    public BaseFooterView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public BaseFooterView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

5、FooterViewListener.class

public interface FooterViewListener {

    /**
     * 網絡不好的時候想要展示的UI
     */
    void onNetChange(boolean isAvailable);

    /**
     * 正常的loading的View
     */
    void onLoadingMore();

    /**
     * 沒有更多數據
     */
    void onNoMore(CharSequence message);

    /**
     *  錯誤時展示的View
     */
    void onError(CharSequence message);
}

6、SimpleFooterView.class

 private TextView mText;

    private ProgressBar progressBar;

    public SimpleFooterView(Context context) {
        this(context, null);
    }

    public SimpleFooterView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SimpleFooterView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        setLayoutParams(new LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_footer_view, this);
        progressBar = (ProgressBar) view.findViewById(R.id.footer_view_progressbar);
        mText = (TextView) view.findViewById(R.id.footer_view_tv);
    }



    @Override
    public void onLoadingMore() {
        progressBar.setVisibility(VISIBLE);
        mText.setVisibility(GONE);
    }

    public void showText(){
        progressBar.setVisibility(GONE);
        mText.setVisibility(VISIBLE);
    }

    /**************文字自行修改或根據傳入的參數動態修改****************/

    @Override
    public void onNoMore(CharSequence message) {
        showText();
        mText.setText("-- the end --");
    }

    @Override
    public void onError(CharSequence message) {
        showText();
        mText.setText("啊哦,好像哪裏不對勁!");
    }

    @Override
    public void onNetChange(boolean isAvailable) {
        showText();
        mText.setText("網絡連接不通暢!");
    }

7、調用

public class SwipeRecyclerViewActivity extends AppCompatActivity {

    private SwipeRecyclerView recyclerView;
    private List<String> data;
    private SwipeRecycleViewAdapter adapter;
    private int pagerSize = 10;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_swipe_recycler_view);

        recyclerView = (SwipeRecyclerView) findViewById(R.id.swipeRecyclerView);

        //set color
        recyclerView.getSwipeRefreshLayout()
                .setColorSchemeColors(Color.RED,Color.YELLOW,Color.BLUE);

        //set layoutManager
        recyclerView.getRecyclerView().setLayoutManager(new LinearLayoutManager(this));
        //recyclerView.getRecyclerView().setLayoutManager(new GridLayoutManager(this, 3));
        //recyclerView.getRecyclerView().setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));

        //禁止下拉刷新
        // recyclerView.setRefreshEnable(false);

        //禁止加載更多
        //recyclerView.setLoadMoreEnable(false);

        //設置emptyView
        /*TextView textView = new TextView(this);
        textView.setText("empty view");
        recyclerView.setEmptyView(textView);*/

        //設置footerView
//        recyclerView.setFooterView(new SimpleFooterView(this));

        //由於SwipeRecyclerView中對GridLayoutManager的SpanSizeLookup做了處理,因此對於使用了
        //GridLayoutManager又要使用SpanSizeLookup的情況,可以這樣使用!
        /*recyclerView.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                return 3;
            }
        });*/

        //設置去除footerView 的分割線
       /* recyclerView.getRecyclerView().addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
                super.onDraw(c, parent, state);
                Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
                paint.setColor(0xFFEECCCC);

                Rect rect = new Rect();
                int left = parent.getPaddingLeft();
                int right = parent.getWidth() - parent.getPaddingRight();
                final int childCount = parent.getChildCount() - 1;
                for (int i = 0; i < childCount; i++) {
                    final View child = parent.getChildAt(i);

                    //獲得child的佈局信息
                    final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    final int top = child.getBottom() + params.bottomMargin;
                    final int itemDividerHeight = 1;//px
                    rect.set(left + 50, top, right - 50, top + itemDividerHeight);
                    c.drawRect(rect, paint);
                }
            }
        });*/

        //設置noMore
        // recyclerView.onNoMore("-- end --");

        //設置網絡處理
        //recyclerView.onNetChange(true);

        //設置錯誤信息
        //recyclerView.onError("error");

        data = new ArrayList<>();
        adapter = new SwipeRecycleViewAdapter(SwipeRecyclerViewActivity.this,data);
        recyclerView.setAdapter(adapter);

        recyclerView.setOnLoadListener(new SwipeRecyclerView.OnLoadListener() {
            @Override
            public void onRefresh() {

                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        data.clear();
                        for (int i = 0; i < pagerSize; i++) {
                            data.add(String.valueOf(i));
                        }

                        recyclerView.complete();
                        adapter.notifyDataSetChanged();

                    }
                }, 1000);

            }

            @Override
            public void onLoadMore() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < pagerSize; i++) {
                            data.add(String.valueOf(i));
                        }

                        if(data.size() > 20){
                            recyclerView.onNoMore("-- the end --");
                        }else {
                            recyclerView.stopLoadingMore();
                            adapter.notifyDataSetChanged();
                        }
                    }
                }, 1000);
            }
        });

        //設置自動下拉刷新,切記要在recyclerView.setOnLoadListener()之後調用
        //因爲在沒有設置監聽接口的情況下,setRefreshing(true),調用不到OnLoadListener
        recyclerView.setRefreshing(true);
    }

}

8、適配器代碼

public class SwipeRecycleViewAdapter extends RecyclerView.Adapter<SwipeRecycleViewAdapter.ItemViewHolder> {

    private Context context;
    private List<String> data;
    private LayoutInflater inflater;

    public SwipeRecycleViewAdapter(Context context, List<String> data) {
        this.context = context;
        this.data = data;
        inflater=LayoutInflater.from(context);
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = inflater.inflate(R.layout.item_swiperecyclerview_item, parent, false);
        ItemViewHolder viewHolder=new ItemViewHolder(view);
        return viewHolder;
    }

    @Override
    public int getItemCount() {
        return data==null?0:data.size();
    }

    @Override
    public void onBindViewHolder(ItemViewHolder holder, final int position) {

        holder.tv.setText(data.get(position));

        holder.tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context, "您點擊的是"+data.get(position), Toast.LENGTH_SHORT).show();
            }
        });
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {

        TextView tv;

        public ItemViewHolder(View view) {
            super(view);
            tv = (TextView) view.findViewById(R.id.tv);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章