SwipeRefreshLayout+RecyclerView 完成下拉刷新,上拉加载更多的自定义控件,简单好用

     下拉刷新,上拉加载更多在许多应用中都频频用到,特别是需要分页加载数据时.之前在项目中经常用到的是网上非常普遍的PullToRefreshLayout.该框架比较好用,不过是比较旧了.就想着重新找一个控件.因为现在android 5.0应用的已经比较多了,所以就想做一个和知乎效果差不多的效果.

      谷歌官方为我们提供一个SwipeRefreshLayout的刷新控件,效果不错,不过仅仅有下拉刷新的效果,上拉加载更多的效果没有提供,需要自己实现.开始我封装了有一个RefreshLayout继承SwipeRefreshLayout,其中的ListView可以做刷新和加载更多,但是暂时不支持RecyclerView,而且有一些Bug.而现在谷歌推荐用RecyclerView替代ListView因此仿照他人的思路重新写了一个,其中SwipeRefreshLayout负责刷新动作,自定义RecyclerView负责加载更多,并且封装了几个适配器.比较简单,目前只是用作展示数据,没有加入侧滑等效果.如下图所示

   

    以下是自定义RecyclerView的代码部分

   

package com.mercury.recyclerload;

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;

/**
 * created by Mercury at 2016/11/20
 * descript:
 */
public class HaoRecyclerView extends RecyclerView {

    private int downY;
    private int moveY;
    private int upY;

    private Context mContext;

    private OnLoadMoreListener mOnLoadMoreListener;
    //是否可加载更多
    private boolean canloadMore = true;
    //真正由界面传入的适配器
    private Adapter mAdapter;
    //封装的适配器,用于适配自定义Recyclerview,包含了点击事件,显示条目的布局及尾布局
    private Adapter mFooterAdapter;
    //是否正在加载数据
    private boolean isLoadingData = false;
    //加载更多的尾布局.继承LinearLayout
    private LoadingMoreFooter loadingMoreFooter;

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

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

    public HaoRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        mContext = context;
        LoadingMoreFooter footView = new LoadingMoreFooter(mContext);
        addFootView(footView);
        footView.setGone();
    }


    //点击监听
    public void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) {
        if (mFooterAdapter != null && mFooterAdapter instanceof FooterAdapter) {
            ((FooterAdapter) mFooterAdapter).setOnItemClickListener(onItemClickListener);
        }
    }


    //长按监听
    public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
        if (mFooterAdapter != null && mFooterAdapter instanceof FooterAdapter) {
            ((FooterAdapter) mFooterAdapter).setOnItemLongClickListener(listener);
        }
    }

    /**
     * 底部加载更多视图
     *
     * @param view
     */
    public void addFootView(LoadingMoreFooter view) {
        loadingMoreFooter = view;
    }

    //设置底部加载中效果
    public void setFootLoadingView(View view) {
        if (loadingMoreFooter != null) {
            loadingMoreFooter.addFootLoadingView(view);
        }
    }

    //设置底部到底了布局
    public void setFootEndView(View view) {
        if (loadingMoreFooter != null) {
            loadingMoreFooter.addFootEndView(view);
        }
    }

    //下拉刷新后初始化底部状态
    public void refreshComplete() {
        if (loadingMoreFooter != null) {
            loadingMoreFooter.setGone();
        }
        isLoadingData = false;
        canloadMore = true;
    }

    public void loadMoreComplete() {
        if (loadingMoreFooter != null) {
            loadingMoreFooter.setGone();
        }
        isLoadingData = false;
    }


    //到底了
    public void loadMoreEnd() {
        if (loadingMoreFooter != null) {
            loadingMoreFooter.setEnd();
        }
    }

    //设置是否可加载更多
    public void setCanloadMore(boolean flag) {
        canloadMore = flag;
    }

    //设置加载更多监听
    public void setOnLoadMoreListener(OnLoadMoreListener listener) {
        mOnLoadMoreListener = listener;
    }

    @Override
    public void setAdapter(Adapter adapter) {
        mAdapter = adapter;
        mFooterAdapter = new FooterAdapter(this,loadingMoreFooter, adapter);
        super.setAdapter(mFooterAdapter);
        mAdapter.registerAdapterDataObserver(mDataObserver);
    }

    @Override
    public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);

        //不在加载数据时,即isLoadingData=false时,才去判断是否满足加载更多的条件
        if (state == RecyclerView.SCROLL_STATE_IDLE && mOnLoadMoreListener != null &&
                !isLoadingData && canloadMore && isPullUp()) {

            LayoutManager layoutManager = getLayoutManager();
            int lastVisibleItemPosition;
            if (layoutManager instanceof GridLayoutManager) {
                lastVisibleItemPosition = ((GridLayoutManager) layoutManager)
                        .findLastVisibleItemPosition();
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
                ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
                lastVisibleItemPosition = last(into);
            } else {
                lastVisibleItemPosition = ((LinearLayoutManager) layoutManager)
                        .findLastVisibleItemPosition();
            }

            if (layoutManager.getChildCount() > 0
                    && lastVisibleItemPosition >= layoutManager.getItemCount() - 1) {
                if (loadingMoreFooter != null) {
                    loadingMoreFooter.setVisible();
                }
                isLoadingData = true;
                mOnLoadMoreListener.onLoadMore();
            }
        }
    }


    //取到最后的一个节点
    private int last(int[] lastPositions) {
        int last = lastPositions[0];
        for (int value : lastPositions) {
            if (value > last) {
                last = value;
            }
        }
        return last;
    }


    private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            mFooterAdapter.notifyDataSetChanged();
        }

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

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

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

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

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            mFooterAdapter.notifyItemMoved(fromPosition, toPosition);
        }
    };

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_UP:
                upY = (int) ev.getRawY();
            default:
                break;

        }
        return super.dispatchTouchEvent(ev);
    }

    //用于区分是不是向上的手势,否则会触发刷新动作
    public boolean isPullUp() {
        return (downY - upY) >= 0;
    }

}

    其中的mAdapter是真正由使用到RecyclerView的组件,比如相应的Activity或Fragment传入的自定义的Adapter.mFooterAdapter是HaoRecyclerView内部独有的适配器,包含了普通条目以及尾布局,并且封装好了点击事件(RecyclerView本身没有点击事件).这样在HaoRecyclerView内部setAdapter其实连接的是FooterAdapter,但是我们真正传入的包含我们数据的适配器是mAdapter,为了解决这个问题.使用RecyclerView包含的AdapterDataObserver来观察FooterAdapter的数据变化,而用mAdapter注册该观察对象.以改变数据.

    下面是自定义的FooterAdapter

package com.mercury.recyclerload;

import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;

/**
 * created by Mercury at 2016/11/20
 * descript: 带脚布局(加载更多)的适配器
 */
public class FooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View
        .OnClickListener, View.OnLongClickListener {

    private RecyclerView.Adapter adapter;

    private HaoRecyclerView haoRecyclerView;

    //自定义的尾布局
    private LoadingMoreFooter loadingMoreFooter;

    private static final int DEFAULT = 0;
    private static final int FOOTER  = -1;

    public FooterAdapter(HaoRecyclerView haoRecyclerView, LoadingMoreFooter loadingMoreFooter,
                         RecyclerView.Adapter adapter) {
        this.haoRecyclerView = haoRecyclerView;
        this.loadingMoreFooter = loadingMoreFooter;
        this.adapter = adapter;
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    return (getItemViewType(position) == RecyclerView.INVALID_TYPE ||
                            getItemViewType(position) == RecyclerView.INVALID_TYPE - 1)
                            ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }

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

    /**
     * 当前布局是否为Footer
     *
     * @param position
     * @return
     */
    public boolean isFooter(int position) {
        return position == getItemCount() - 1;
    }


    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == FOOTER) {
            return new SimpleViewHolder(loadingMoreFooter);
        }
        return adapter.onCreateViewHolder(parent, viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        holder.itemView.setOnClickListener(this);
        holder.itemView.setOnLongClickListener(this);
        if (adapter != null) {
            int count = adapter.getItemCount();
            if (position < count) {
                adapter.onBindViewHolder(holder, position);
                return;
            }
        }
    }

    @Override
    public int getItemCount() {
        if (adapter != null) {
            return 1 + adapter.getItemCount();
        } else {
            return 1;
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (isFooter(position)) {
            return FOOTER;
        }
        if (adapter != null) {
            int count = adapter.getItemCount();
            if (position < count) {
                return adapter.getItemViewType(position);
            }
        }
        return DEFAULT;
    }

    @Override
    public long getItemId(int position) {
        if (adapter != null && position >= 0) {
            int adapterCount = adapter.getItemCount();
            if (position < adapterCount) {
                return adapter.getItemId(position);
            }
        }
        return -1;
    }

    @Override
    public void onClick(View view) {
        int pos = haoRecyclerView.getChildAdapterPosition(view);
        if (onItemClickListener != null && !isFooter(pos)) {
            onItemClickListener.onItemClick(null, view, pos, 0);

        }
    }


    @Override
    public boolean onLongClick(View view) {
        int pos = haoRecyclerView.getChildAdapterPosition(view);
        if (onItemLongClickListener != null && !isFooter(pos)) {
            onItemLongClickListener.onItemLongClick(null, view, pos, 0);

        }
        return true;
    }

    //点击
    AdapterView.OnItemClickListener onItemClickListener;

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

    //长按
    AdapterView.OnItemLongClickListener onItemLongClickListener;

    public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener
                                                   onItemLongClickListener) {
        this.onItemLongClickListener = onItemLongClickListener;
    }

    private class SimpleViewHolder extends RecyclerView.ViewHolder {
        public SimpleViewHolder(View itemView) {
            super(itemView);
        }
    }
}

    这里我使用的尾布局样式非常简单,加载中就是一个居中的ProgressBar,封装了几种判断.包括下拉刷新及上拉加载完成后隐藏该尾布局,加载进行中显示该布局.没有更多数据的时候显示哪个布局,并且如果是尾布局的话,该条目应该是不可点击的,或者点击后的逻辑单独处理.更多逻辑还要进一步完善
package com.mercury.recyclerload;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * created by Mercury at 2016/11/21
 * descript: 加载更多的底部布局
 */
public class LoadingMoreFooter extends LinearLayout {

    private Context      context;
    private LinearLayout loading_view_layout;
    private LinearLayout end_layout;

    public LoadingMoreFooter(Context context) {
        super(context);
        this.context = context;
        initView(context);
    }

    /**
     * @param context
     * @param attrs
     */
    public LoadingMoreFooter(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public void initView(Context context) {
        setGravity(Gravity.CENTER);
        setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        View view = layoutInflater.inflate(R.layout.footer_layout, null);
        loading_view_layout = (LinearLayout) view.findViewById(R.id.loading_view_layout);
        end_layout = (LinearLayout) view.findViewById(R.id.end_layout);


        addFootLoadingView(new ProgressBar(context, null, android.R.attr.progressBarStyle));

        TextView textView = new TextView(context);
        textView.setText("已经到底啦~");
        addFootEndView(textView);

        addView(view);
    }


    //设置底部加载中效果
    public void addFootLoadingView(View view) {
        loading_view_layout.removeAllViews();
        loading_view_layout.addView(view);
    }

    //设置底部到底的布局
    public void addFootEndView(View view) {
        end_layout.removeAllViews();
        end_layout.addView(view);
    }


    //设置已经没有更多数据
    public void setEnd() {
        setVisibility(VISIBLE);
        loading_view_layout.setVisibility(GONE);
        end_layout.setVisibility(VISIBLE);
    }


    public void setVisible(){
        setVisibility(VISIBLE);
        loading_view_layout.setVisibility(VISIBLE);
        end_layout.setVisibility(GONE);

    }


    public void setGone(){
        setVisibility(GONE);
    }

}

   我封装了一个基类,真正填充数据的所有使用该控件的适配器均可继承该基类.
package com.mercury.recyclerload;

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

import java.util.ArrayList;
import java.util.List;

/**
 * created by Mercury at 2016/11/21
 * descript:    提供给RecyclerView适配器使用的基类
 */

public abstract class BaseRecylerAdapter<T> extends RecyclerView.Adapter {

    public Context mContext;

    public List<T> mData = new ArrayList<>();

    public LayoutInflater mInflater;


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

    public List<T> getData() {
        return mData;
    }

    public void setData(List<T> list) {
        this.mData.clear();
        this.mData.addAll(list);
        notifyDataSetChanged();
    }

    public void addAll(List<T> list) {
        int lastIndex = this.mData.size();
        if (this.mData.addAll(list)) {
            notifyItemRangeInserted(lastIndex, list.size());
        }
    }

    public void removePos(int position) {
        if(this.mData.size() > 0) {
            mData.remove(position);
            notifyItemRemoved(position);
        }

    }

    public void remove(Object obj) {
        if(this.mData.size() > 0) {
            mData.remove(obj);
        }

    }

    public void clear() {
        mData.clear();
        notifyDataSetChanged();
    }

}

    最后附上动图所展示的Demo,为了达成目前的效果,在代码中需要自己调用控件中暴露的一些方法去判断,如Demo的onRefresh和onLoadMore的回调中所示:
package com.mercury.recyclerload;

import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.view.View;
import android.widget.AdapterView;

import java.util.ArrayList;
import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout
        .OnRefreshListener, OnLoadMoreListener {

    @Bind(R.id.rv)
    HaoRecyclerView    rv;
    @Bind(R.id.swr)
    SwipeRefreshLayout swr;

    List<String> list;
    MyAdapter    mAdapter;
    private Handler mHandler;

    private int a ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        initData();
        initEvent();

    }

    private void initEvent() {
        rv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                ToastUtil.showToast(MainActivity.this, position + "");
            }
        });
    }

    private void initData() {
        mHandler = new Handler();
        swr.setColorSchemeResources(R.color.colorPrimary);
        swr.setOnRefreshListener(this);
        rv.setOnLoadMoreListener(this);

        list = new ArrayList<>();
        rv.setLayoutManager(new LinearLayoutManager(this));
        initList();
        mAdapter = new MyAdapter(this);
        mAdapter.setData(list);
        rv.setAdapter(mAdapter);

    }

    private void initList() {
        list.clear();
        for (int i = 0; i < 10; i++) {
            list.add(i + "");
        }
    }

    @Override
    public void onRefresh() {
        a = 0;
        rv.setCanloadMore(false);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                initList();
                mAdapter.setData(list);
                rv.refreshComplete();
                swr.setRefreshing(false);
            }
        }, 1500);
    }

    @Override
    public void onLoadMore() {
        addData();
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (mAdapter.getItemCount() == 30) {
                    rv.loadMoreEnd();
                    return;
                }
                mAdapter.addAll(list);
                rv.loadMoreComplete();
            }
        }, 1500);

    }

    private void addData() {
        a += 10;
        list.clear();
        for (int i = a; i < a + 10; i++) {
            list.add(i + "");
        }
    }
}

源码下载



   


猜你喜欢

转载自blog.csdn.net/wzhseu/article/details/53313862