RefreshRecyclerView下拉刷新,加载更多

来源:互联网 发布:虚拟串口软件是什么 编辑:程序博客网 时间:2024/05/18 03:18

ListView已经用了很多年了,后来又有了RecyclerView,基本可以代替ListView/GridView了,还有瀑布流的模式,加上各种特效,于是就尝试用RecyclerView替代listview。

如果UI要求不严格,那么有一个很简单的方式实现下拉刷新:SwipeRefreshLayout  (具体介绍参考:http://blog.csdn.net/dalancon/article/details/46125667),然后基本你就可以开开心心做完了。

但是如果你们的UI或者产品说,我们要一个什么什么样的功能(反正就是不造轮子不行的需求),那只能自己做了。

先看一下效果:



简单说一下思路:

下拉刷新:

1.自定义一个线性布局linearlayout,里面先添加一个header,然后添加recycclerview。

2.计算出header的高度h1,然后设置linearlayout布局的的padding为-h1(也可以设置margin为-h,都可以实现,本文采用的是padding)

3.利用消息传递机制及对recyclerview的滚动监听来判断是否可以下拉刷新。

4.根据用户的手势移动来设置padding值,再加上一些回滚效果就ok了。


加载更多:

1.首先写一个BaseAdapter抽象类继承

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

2.在BaseAdapter中添加一个footerViewHolder,并设置开关,如果有加载更多的时候,就显示。如果没有,则不显示。

3.监听RecyclerView的滚动,如果滚动到末尾并且有加载更多,则调用加载更多的方法。


有了上面的思路写起来就简单多了。


接下来分析代码:

下拉刷新:

public class RefreshRecyclerView extends LinearLayout{    public static final long ONE_MINUTE = 60 * 1000;//一分钟的毫秒值,用于判断上次的更新时间    public static final long ONE_HOUR = 60 * ONE_MINUTE;//一小时的毫秒值,用于判断上次的更新时间    public static final long ONE_DAY = 24 * ONE_HOUR;//一天的毫秒值,用于判断上次的更新时间    public static final long ONE_MONTH = 30 * ONE_DAY;//一月的毫秒值,用于判断上次的更新时间    public static final long ONE_YEAR = 12 * ONE_MONTH;//一年的毫秒值,用于判断上次的更新时间    private String MYRECYCLERVIEW = "MyRecyclerView";//保存用的    private int id;//用来区分不同页面    private LinearLayout headerView;//刷新头部布局    public static final int SCROLL_SPEED = -10;//下拉头部回滚的速度    private int topPadding;//距离顶部的padding值    private RecyclerView mRecyclerView;//列表控件    private LinearLayoutManager mLayoutManager;//RecyclerView布局管理器    private int firstVisibleItemPostion;//第一个可见item的position    private int mHeaderHeight;//头部高度    private boolean hasMore;//设置是否有加载更多(用户设置)    private boolean hasRefresh;//设置是否有下拉刷新(用户设置)    private boolean canRefresh;//是否可以下拉刷新(逻辑判断)    private boolean isLoading;//列表是否正在刷新或加载更多中    private boolean canScroll;//列表是否可以滚动(刷新加载中禁止用户进行列表操作)    private LoadLinsteners loadLinsteners;//加载监听器    private ScrollLinsteners scrollLinsteners;//滚动监听器    private BaseAdapter mAdapter;//Item适配器    //头部相关    private TextView description, updated_at;//描述    private Long lastUpdateTime;//上次更新时间    private ImageView arrow;//箭头    private ProgressBar progress_bar;    private ImageView iv_head;//头部图片    private Context context;    private int readCount;//判断当前列表最大滚动位置    public RefreshRecyclerView(Context context) {        this(context, null);    }    public RefreshRecyclerView(Context context, AttributeSet attrs) {        super(context, attrs);        this.context = context;        initContents();    }    /**     * 初始化     */    private void initContents() {        //默认可下拉刷新,无加载更多        canRefresh = true;        hasMore = false;        hasRefresh = true;        isLoading = false;        canScroll = true;        //头部        headerView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.item_recyler_header, null);        iv_head = (ImageView)headerView.findViewById(R.id.iv_head);        description = (TextView)headerView.findViewById(R.id.description);        updated_at = (TextView)headerView.findViewById(R.id.updated_at);        arrow = (ImageView)headerView.findViewById(R.id.arrow);        progress_bar = (ProgressBar)headerView.findViewById(R.id.progress_bar);        //测量并获取头部高度        measureView(headerView);        mHeaderHeight = headerView.getMeasuredHeight();        //列表        mRecyclerView = new RecyclerView(context);        mRecyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);        //创建默认的线性LayoutManager        mLayoutManager = new LinearLayoutManager(getContext());        mRecyclerView.setLayoutManager(mLayoutManager);        //添加布局        setOrientation(VERTICAL);        addView(headerView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));        addView(mRecyclerView, new LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1));        //隐藏头部        setTopPadding(-mHeaderHeight);        setLinsteners();        /**         * 添加旋转动画         */        Animation animation= AnimationUtils.loadAnimation(context, R.anim.anim_rotate_earth);        LinearInterpolator lin = new LinearInterpolator();        animation.setInterpolator(lin);        iv_head.startAnimation(animation);    }    /**     * 设置布局管理器     * @param layoutManager     */    public void setmLayoutManager(LinearLayoutManager layoutManager){        this.mLayoutManager = layoutManager;        mRecyclerView.setLayoutManager(mLayoutManager);    }    /**     * 获取布局管理器     * @return     */    public RecyclerView.LayoutManager getmLayoutManager(){        return mLayoutManager;    }    /**     * 设置adapter     * @param mAdapter     */    public void setAdapter(BaseAdapter mAdapter){        this.mAdapter = mAdapter;        mRecyclerView.setAdapter(mAdapter);    }    /**     * 获取recyclerview     * @return     */    public RecyclerView getmRecyclerView(){        return mRecyclerView;    }    /**     * 加载监听器     */    public interface LoadLinsteners{        void onLoadMore();        void onRefresh();    }    /**     * 设置监听器     * @param loadLinsteners     * @param id     */    public void setLoadLinsteners(LoadLinsteners loadLinsteners, int id){        this.loadLinsteners = loadLinsteners;        this.id = id;        refreshUpdatedAtValue();    }    /**     * 设置监听器     * @param loadLinsteners     */    public void setLoadLinsteners(LoadLinsteners loadLinsteners){        this.loadLinsteners = loadLinsteners;        this.id = -1;        refreshUpdatedAtValue();    }    /**     * 滚动监听接口     */    public  interface ScrollLinsteners{        void onScrolled(int firstVisibleItem, int dx, int dy);    }    /**     * 设置滚动监听     * @param scrollLinsteners     */    public void setScrollLinsteners(ScrollLinsteners scrollLinsteners){        this.scrollLinsteners = scrollLinsteners;    }    /**     * 获取最大滚动位置     * @return     */    public int getReadCount(){        return readCount;    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        if(!canScroll) {//禁止事件传递            return true;        }else {            return false;        }    }    private float moveY, startY = -1, dY;    private void setLinsteners() {        /**         * 添加触摸监听,实现下拉效果         */        mRecyclerView.setOnTouchListener(new OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                if (canRefresh && firstVisibleItemPostion == 0 && !isLoading && hasRefresh) {                    switch (event.getAction()) {                        case MotionEvent.ACTION_DOWN:                            startY = -1;                            Log.i("move---", "down:-1");                            break;                        case MotionEvent.ACTION_MOVE:                            moveY = event.getRawY();                            Log.i("move----1", "startY:"+ startY + "  moveY:" + moveY + "  dY:" + dY + "  headerHeight:" + mHeaderHeight);                            if(startY == -1) {                                startY = moveY;                            }                            dY = moveY - startY;                            Log.i("move----2", "startY:"+ startY + "  moveY:" + moveY + "  dY:" + dY + "  headerHeight:" + mHeaderHeight);                            if (dY > 30) {                                dY = dY - 30;                                int maxLength = mHeaderHeight * 6;                                if (dY < maxLength && dY >= 30) {                                    description.setText(R.string.pull_to_refresh);                                } else if (dY >= maxLength) {                                    description.setText(R.string.release_to_refresh);                                    dY = maxLength;                                }                                if(dY > mHeaderHeight){                                    dY = (dY + mHeaderHeight)/2;                                }                                setTopPadding((int)(dY - mHeaderHeight));                            } else {                                Log.i("move---", "reset");                                setTopPadding(- mHeaderHeight);                                return false;                            }                            break;                        case MotionEvent.ACTION_UP:                            Log.i("move---", "up:-1");                            if (dY > mHeaderHeight / 2) {                                isLoading = true;                                canScroll = false;                                canRefresh = false;                                description.setText(R.string.refreshing);                                arrow.setVisibility(View.GONE);                                progress_bar.setVisibility(View.VISIBLE);                                new RefreshingTask().execute();                                if(loadLinsteners != null) {                                    loadLinsteners.onRefresh();                                }                            } else {                                startY = -1;                                new HideHeaderTask().execute();                                return false;                            }                            dY = 0;                            startY = -1;                            moveY = 0;                            break;                    }                    return true;                }                return false;            }        });        /**         * 添加滚动监听,判断加载更多         */        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {            @Override            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {                super.onScrolled(recyclerView, dx, dy);                firstVisibleItemPostion = mLayoutManager.findFirstVisibleItemPosition();                int lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();                int totalItemCount = mLayoutManager.getItemCount();                if (readCount <= lastVisibleItem) {                    readCount = lastVisibleItem;                }                //lastVisibleItem >= totalItemCount - 4 表示剩下4个item自动加载                // dy > 0 表示向下滑动                if (lastVisibleItem >= totalItemCount - 4 && dy >= 0) {                    if (hasMore && !isLoading) {//可以下拉刷新并且没有加载中                        isLoading = true;                        canScroll = true;                        if (loadLinsteners != null) {                            loadLinsteners.onLoadMore();                        }                    }                }                //如果列表在头部                if (firstVisibleItemPostion == 0 && mLayoutManager.getChildAt(0).getTop() == 0 && hasRefresh) {                    Log.i("move---", "recyclerview:reset:-1");                    canRefresh = true;                    startY = -1;                } else {                    canRefresh = false;                }                if (scrollLinsteners != null) {                    scrollLinsteners.onScrolled(firstVisibleItemPostion, dx, dy);                }            }            @Override            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {                super.onScrollStateChanged(recyclerView, newState);            }        });    }    /**     * 设置顶部padding值     */    public void setTopPadding(int top){        topPadding = top;        setPadding(0, top, 0, 0);    }    /**     * 停止下拉刷新     */    public void onStopRefresh(){        new HideHeaderTask().execute();        canRefresh = true;        isLoading = false;        canScroll = true;        arrow.setVisibility(View.GONE);        progress_bar.setVisibility(View.GONE);        saveTv_refresh_time();    }    /**     * 停止加载更多     */    public void onStopMore(){        isLoading = false;    }    /**     * 保存刷新时间     */    public void saveTv_refresh_time(){        if(id == -1)return;        saveSPLongData(MYRECYCLERVIEW + id, System.currentTimeMillis());        refreshUpdatedAtValue();    }    public void setId(int id){        this.id = id;    }    /**     * 是否可以加载更多     * @param hasMore     */    public void setHasMore(boolean hasMore){        this.hasMore = hasMore;        if(mAdapter != null) {            mAdapter.setHasMore(hasMore);        }    }    /**     * 是否可以下拉刷新     * @param hasRefresh     */    public void setHasRefresh(boolean hasRefresh){        this.hasRefresh = hasRefresh;    }    /**     * 测量布局     */    private void measureView(View child) {        ViewGroup.LayoutParams params = child.getLayoutParams();        if (params == null) {            params = new ViewGroup.LayoutParams(                    ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.WRAP_CONTENT);        }        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, params.width);        int lpHeight = params.height;        int childHeightSpec;        if (lpHeight > 0) {            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);        } else {            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);        }        child.measure(childWidthSpec, childHeightSpec);    }    /**     * 刷新下拉头中上次更新时间的文字描述。     */    private void refreshUpdatedAtValue() {        if(id == -1)return;        lastUpdateTime = getSPLongData(MYRECYCLERVIEW + id);        long currentTime = System.currentTimeMillis();        long timePassed = currentTime - lastUpdateTime;        long timeIntoFormat;        String updateAtValue;        if (lastUpdateTime == -1) {            updateAtValue = getResources().getString(R.string.not_updated_yet);        } else if (timePassed < 0) {            updateAtValue = getResources().getString(R.string.time_error);        } else if (timePassed < ONE_MINUTE) {            updateAtValue = getResources().getString(R.string.updated_just_now);        } else if (timePassed < ONE_HOUR) {            timeIntoFormat = timePassed / ONE_MINUTE;            String value = timeIntoFormat + "分钟";            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);        } else if (timePassed < ONE_DAY) {            timeIntoFormat = timePassed / ONE_HOUR;            String value = timeIntoFormat + "小时";            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);        } else if (timePassed < ONE_MONTH) {            timeIntoFormat = timePassed / ONE_DAY;            String value = timeIntoFormat + "天";            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);        } else if (timePassed < ONE_YEAR) {            timeIntoFormat = timePassed / ONE_MONTH;            String value = timeIntoFormat + "个月";            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);        } else {            timeIntoFormat = timePassed / ONE_YEAR;            String value = timeIntoFormat + "年";            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);        }        updated_at.setText(updateAtValue);    }    /**     * 正在刷新的任务,在此任务中会去回调注册进来的下拉刷新监听器。     */    class RefreshingTask extends AsyncTask<Void, Integer, Integer> {        @Override        protected Integer doInBackground(Void... params) {            int tp = topPadding;            long curMills = System.currentTimeMillis();            while (true) {                long nowMills = System.currentTimeMillis();                if(nowMills - curMills >= 10){                    curMills = nowMills;                    tp = tp + SCROLL_SPEED * 2;                    if (tp <= 0) {                        tp = 0;                        break;                    }                    publishProgress(tp);                }            }            return tp;        }        @Override        protected void onProgressUpdate(Integer... tp) {            setTopPadding(tp[0]);        }        @Override        protected void onPostExecute(Integer tp) {            setTopPadding(tp);        }    }    /**     * 隐藏下拉头的任务,当未进行下拉刷新或下拉刷新完成后,此任务将会使下拉头重新隐藏。     */    class HideHeaderTask extends AsyncTask<Void, Integer, Integer> {        @Override        protected Integer doInBackground(Void... params) {            int tp = topPadding;            long curMills = System.currentTimeMillis();            while (true) {                long nowMills = System.currentTimeMillis();                if(nowMills - curMills >= 10) {                    curMills = nowMills;                    tp = tp + SCROLL_SPEED;                    if (tp <= -mHeaderHeight) {                        tp = -mHeaderHeight;                        break;                    }                    publishProgress(tp);                }            }            return tp;        }        @Override        protected void onProgressUpdate(Integer... tp) {            setTopPadding(tp[0]);        }        @Override        protected void onPostExecute(Integer tp) {            setTopPadding(tp);        }    }    /**     * 获取账户管理sp     * @return     */    private SharedPreferences getAccountSP() {        return context.getSharedPreferences("account", Context.MODE_PRIVATE);    }    /**     * 保存Long值     * @param key     * @param value     */    public void saveSPLongData(String key, long value) {        getAccountSP().edit().putLong(key, value).commit();    }    /**     * 获取存储的Long值     * @param key     * @return     */    public Long getSPLongData(String key) {        return getAccountSP().getLong(key, -1);    }}

(已经很详细的注释了,就不多说了,这里有一个注意点就是:

dependencies {    compile fileTree(dir: 'libs', include: ['*.jar'])    testCompile 'junit:junit:4.12'    compile 'com.android.support:appcompat-v7:23.3.0'    <span style="color:#ff0000;">compile 'com.android.support:design:23.2.0'</span>}
红色区域是需要注意的,如果你用的是23.3.0会有问题。。。原因未知。。。其它版本未测试)


加载更多:

public abstract class BaseAdapter<T,K extends ViewHolder> extends Adapter<K> {    protected final LayoutInflater layoutInflater;    public List<T> mMessages;    private static final int TYPE_FOOTER = -2;    public boolean hasMore;    public Context context;    private int  mResources;    private ItemOnclickLinstener itemOnclickLinstener;    public BaseAdapter(Context context, List<T> mMessages, int mResources) {        this.mMessages = mMessages;        this.context = context;        this.mResources = mResources;        layoutInflater = LayoutInflater.from(context);        hasMore = false;    }    @Override    public K onCreateViewHolder(ViewGroup parent, int viewType) {        // type == TYPE_FOOTER 返回footerView        if (viewType == TYPE_FOOTER) {            View view = layoutInflater.inflate(R.layout.item_footer, null);            view.setLayoutParams(new ViewGroup.LayoutParams(                    ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.WRAP_CONTENT));            return (K)new FooterViewHolder(view);        }else {            final K vh = createViewHolder(viewType, parent);            vh.itemView.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    if(itemOnclickLinstener != null) {                        itemOnclickLinstener.setItemOnclickLinsteners(v, vh.getLayoutPosition(), vh.getItemViewType());                    }                }            });            return vh;        }    }    @Override    public void onBindViewHolder(final K viewHolder, final int position) {        if(!(hasMore && position == getItemCount() -1)){            setOnBindViewHolder(viewHolder, position);        }    }    @Override    public int getItemCount() {        int size = 0;        if(mMessages != null){            size = mMessages.size();        }        return hasMore ? size + 1 : size;    }    @Override    public int getItemViewType(int position) {        if (position + 1 == getItemCount() && hasMore) {            return TYPE_FOOTER;        } else {            return setItemViewType(position);        }    }    /**     * footer ViewHolder     */    class FooterViewHolder extends ViewHolder {        public FooterViewHolder(View view) {            super(view);        }    }    /**     * 是否显示加载更多     * @param hasMore     */    public void setHasMore(boolean hasMore){        this.hasMore = hasMore;    }    /**     * 创建ViewHolder     * @param viewType     * @return     */    protected abstract K createViewHolder(int viewType, ViewGroup parent);    /**     * 设置ViewHolder类型     * @param position     * @return     */    protected abstract int setItemViewType(int position);    /**     * 数据填充     */    protected abstract void setOnBindViewHolder(K viewHolder, int position);    /**     * Item点击事件     * @param     * @return     */    public void setItemOnClickLinsteners(ItemOnclickLinstener itemOnclickLinstener){        this.itemOnclickLinstener = itemOnclickLinstener;    }    /**     * item点击接口     */    public interface ItemOnclickLinstener{        void setItemOnclickLinsteners(View v, int postion, int type);    }    protected View getView(int resource){        View view = layoutInflater.inflate(resource, null);        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                ViewGroup.LayoutParams.WRAP_CONTENT));        return view;    }    protected View getView(){        View view = layoutInflater.inflate(mResources, null);        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                ViewGroup.LayoutParams.WRAP_CONTENT));        return view;    }    protected View getView(ViewGroup parent){        View view = layoutInflater.inflate(mResources, parent, false);        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                ViewGroup.LayoutParams.WRAP_CONTENT));        return view;    }}

只需要继承此类即可实现加载更多的功能。

RecyclerView卡顿优化(1)主要介绍了基于此进行的进一步优化,源码中也已经更新了最新的优化功能。


源码地址:RefreshRecyclerView源码


0 0
原创粉丝点击