实现下拉刷新和加载更多的ListView就这么简单

作者: 付三君 | 来源:发表于2016-06-22 22:17 被阅读5581次

    概述

    ListView绝对是我们开发中的高频的控件。目前很多人建议使用ReclyeView替换ListViewRecycleView更加灵活,支持丰富的布局(横向列表,竖线列表,瀑布流,网格等),RecycleView.AdaperBaseAdapter做了更好的封装,但是有不少基础功能需要自己实现,如分割线,点击等等。故在实现简单列表页时我们还是优先选择ListView。通常需要支持下拉刷新,重新加载第一页数据;滑动到底部或点击加载更多时,加载下一页的数据;在没有更多数据时,还可提醒用户。github应该有很多酷炫项目支持上述功能,但是跟自己预期的或多或少有些出入。

    1. 只支持下拉刷新
    2. 封装太复杂,支持太多,很多或许用不到

    分析

    下拉刷新加载更多 是两个功能,官方v4库的SwipeRefreshLayout已经支持下拉刷新功能,只需在ListView外嵌SwipeRefreshLayout即可。那剩下的问题就是如何实现加载更多功能。我们先思考加载更多触发的时机是什么?

    1. 滚动到底部,自动触发或者点击加载更多或者上拉刷新
    2. 若加载显示的数据未铺满一屏幕,未出现滚动时,第一种情况就触发不了。咋办?这时列表底部应该有一个点击加载跟多控件。

    上拉刷新实现比较麻烦,且交互体验不佳。我更加推崇滚动到底部自动加载,但是某些场景(如加载耗很多流量)可能不是很适合。可以优化为前N页自动加载,之后只能点击加载更多控件来加载下一页数据。数据集通常是有限的,加载到最后一页时应该提示用户,并且不再可触发加载下一页。

    实现

    直接上代码:

    public class SimpleListView extends SwipeRefreshLayout {
        private ListView mListView;
        private LoadMoreStatus mLoadMoreStatus = LoadMoreStatus.CLICK_TO_LOAD;
        private OnLoadListener mOnLoadListener;
        private TextView mLoadMoreView;
        private AbsListView.OnScrollListener mOnScrollListener;
        private View mEmptyView;
        private ListAdapter mAdapter;
    
        /**
         * 加载更多状态
         */
        public static enum LoadMoreStatus {
            /**
             * 点击加载更多
             */
            CLICK_TO_LOAD,
            /**
             * 正在加载
             */
            LOADING,
            /**
             * 没有更多内容了
             */
            LOADED_ALL
        }
    
        /**
         * 加载监听器
         */
        public static interface OnLoadListener {
            /**
             * 下来刷新或者加载更多时触发该回调
             *
             * @param isRefresh true为下拉刷新 false为加载更多
             */
            public void onLoad(boolean isRefresh);
        }
    
        public SimpleListView(Context context) {
            super(context);
            init(context, null);
        }
    
        public SimpleListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context, attrs);
        }
    
        private void init(Context context, AttributeSet attrs) {
            mListView = new ListView(context, attrs);
            addView(mListView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
                private boolean mIsEnd = false;
    
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    if (mOnScrollListener != null) {
                        mOnScrollListener.onScrollStateChanged(view, scrollState);
                    }
                    if (scrollState == SCROLL_STATE_IDLE) {
                        //1:到达底部 2:底部当前可以加载更多 3:顶部不在刷新中状态
                        if (mIsEnd && mLoadMoreStatus == LoadMoreStatus.CLICK_TO_LOAD && !isRefreshing()) {
                            setLoadMoreStatus(LoadMoreStatus.LOADING);
                            if (mLoadMoreStatus != null) {
                                mOnLoadListener.onLoad(false);
                            }
                        }
                    }
                }
    
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                    if (mOnScrollListener != null) {
                        mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
                    }
                    if (firstVisibleItem + visibleItemCount >= totalItemCount - 1) {
                        mIsEnd = true;
                    } else {
                        mIsEnd = false;
                    }
                }
            });
    
            super.setOnRefreshListener(new OnRefreshListener() {
                @Override
                public void onRefresh() {
                    if (mLoadMoreStatus != LoadMoreStatus.LOADING) {
                        if (mOnLoadListener != null) {
                            mOnLoadListener.onLoad(true);
                        }
                    } else {
                        SimpleListView.super.setRefreshing(false);
                    }
                }
            });
    
        }
    
        public void addHeaderView(View view) {
            mListView.addHeaderView(view);
        }
    
        public void addHeaderView(View v, Object data, boolean isSelectable) {
            mListView.addHeaderView(v, data, isSelectable);
        }
    
        public void addFooterView(View view) {
            mListView.addFooterView(view);
        }
    
        public void addFooterView(View v, Object data, boolean isSelectable) {
            mListView.addFooterView(v, data, isSelectable);
        }
    
        public void setOnScrollListener(AbsListView.OnScrollListener listener) {
            mOnScrollListener = listener;
        }
    
        public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
            mListView.setOnItemClickListener(listener);
        }
    
        public void setEmptyView(View emptyView) {
            if (emptyView != null) {
                mEmptyView = emptyView;
                if (mAdapter != null && mAdapter.getCount() > 0) {
                    mEmptyView.setVisibility(View.GONE);
                } else {
                    mEmptyView.setVisibility(View.VISIBLE);
                }
    //            mListView.setEmptyView(emptyView);
            }
        }
    
        @Override
        @Deprecated
        public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        }
    
        @Override
        @Deprecated
        public void setRefreshing(boolean refreshing) {
        }
    
        public void setAdapter(final ListAdapter adapter) {
            if (adapter == null) {
                return;
            }
            mAdapter = adapter;
            if (mLoadMoreView == null) {
                mLoadMoreView = new TextView(getContext());
                mLoadMoreView.setTextColor(0xff333333);
                mLoadMoreView.setTextSize(14);
                mLoadMoreView.setGravity(Gravity.CENTER);
                int count = adapter.getCount();
                mLoadMoreView.setVisibility(count == 0 ? View.GONE : View.VISIBLE);
                if (mEmptyView != null) {
                    mEmptyView.setVisibility(count == 0 ? View.VISIBLE : View.GONE);
                }
                mLoadMoreView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (mLoadMoreStatus == LoadMoreStatus.CLICK_TO_LOAD && !isRefreshing()) {
                            setLoadMoreStatus(LoadMoreStatus.LOADING);
                            if (mLoadMoreStatus != null) {
                                mOnLoadListener.onLoad(false);
                            }
                        }
                    }
                });
                mLoadMoreView.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, getResources().getDimensionPixelOffset(R.dimen.dp10) * 4));
                mListView.addFooterView(mLoadMoreView);
            }
            mListView.setAdapter(adapter);
            adapter.registerDataSetObserver(new DataSetObserver() {
                @Override
                public void onChanged() {
                    int count = adapter.getCount();
                    mLoadMoreView.setVisibility(count == 0 ? View.GONE : View.VISIBLE);
                    if (mEmptyView != null) {
                        mEmptyView.setVisibility(count == 0 ? View.VISIBLE : View.GONE);
                    }
                }
            });
        }
    
        private void setLoadMoreStatus(LoadMoreStatus status) {
            mLoadMoreStatus = status;
            if (mLoadMoreView != null) {
                if (mLoadMoreStatus == LoadMoreStatus.LOADED_ALL) {
                    mLoadMoreView.setText("没有更多内容了");
                } else if (mLoadMoreStatus == LoadMoreStatus.LOADING) {
                    mLoadMoreView.setText("正在加载...");
                } else {
                    mLoadMoreView.setText("点击加载更多");
                }
            }
        }
    
        public void setOnLoadListener(OnLoadListener listener) {
            mOnLoadListener = listener;
        }
    
        public void finishLoad(boolean loadAll) {
            super.setRefreshing(false);
            setLoadMoreStatus(loadAll ? LoadMoreStatus.LOADED_ALL : LoadMoreStatus.CLICK_TO_LOAD);
        }
    
    }
    

    基本思路如下:

    1. 继承SwipeRefreshLayout,内嵌一个ListView
    2. ListView添加一个footerView,做为加载更多控件mLoadMoreView
    3. mLoadMoreView有三种状态点击加载更多正在加载没有更多内容
    4. finishLoad(boolean loadAll) 完成数据加载,关闭加载状态时调用,其中 loadAll表示是否加载完所有数据。
    5. OnLoadListener接口,下来刷新或者加载更多时触发该回调,其中的方法是

    isRefresh true为下拉刷新 false为加载更多
    public void onLoad(boolean isRefresh);

    还有一些实现细节不再明说,有兴趣的同学可以运行一下demo

    相关文章

      网友评论

        本文标题:实现下拉刷新和加载更多的ListView就这么简单

        本文链接:https://www.haomeiwen.com/subject/wgfydttx.html