自定义SwipeRefreshLayout 支持 list 滑动到底部自动加载更多

android 5.0 新出了下拉刷新控件 SwipeRefreshLayout,现在被更多公司使用。

但是控件不支持上拉加载,现在也有第三方框架支持,自己有兴趣写了一点测试代码支持SwipeRefreshLayout 嵌套 listView 第一页手动点击加载,其他页滑动到底自动加载数据。

首先要获取到  listView (目前支持listView 后续可能会增加 多其他控件的支持)


        int childCount = getChildCount();
        log(childCount);
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof ListView) {
                listView = (ListView) childView;
                break;
            }
        }

初始化  footerView 并设置点击事件的回调

  if (footer == null) {
            int footerViewsCount = listView.getFooterViewsCount();
            log("footerViewsCount = " + footerViewsCount);
            if (footerViewsCount == 0) {
                LayoutInflater inflater = LayoutInflater.from(getContext());
                footer = inflater.inflate(R.layout.list_footer, null);
                listView.addFooterView(footer);
                footerProgress = footer.findViewById(R.id.footer_progress_bar);
                footerText = footer.findViewById(R.id.footer_content);
                footerText.setText(R.string.click_load_update);
                footer.setVisibility(View.GONE);
            }
            footerText.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (type == 2) {
                        loadDataCallBack();
                    }
                }
            });
        }

重点就是对 listView 的滑动监听

listView.setOnScrollListener(new AbsListView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(AbsListView absListView, int i) {

                }

                /**
                 * 滑动监听
                 * @param absListView listView 的父类
                 * @param firstVisibleItem 屏幕可见第一个 item 位置(不一定是 0)
                 * @param visibleItemCount 屏幕可见的最后一个 item 与 firstVisibleItem 相隔多少个 item
                 * @param totalCount listView 的 item 总数包括添加的 headView、footerView
                 */
                @Override
                public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalCount) {
//                    log("onScroll firstVisibleItem = " + firstVisibleItem + "visibleItemCount = " + visibleItemCount + "totalCount = " + totalCount);
                    //数据不在加载中
                    int countChange = totalCount - historyTotalCount;
                    if (!loadDataNow && countChange != 0) {
                        //如果是第一页刷新后加载数据回调加载数据接口
                        if (removeFooter && countChange == 1) {
                            removeFooter = false;
                            return;
                        }
                        //有新数据加载
                        log("countChange= " + countChange);
                        if (countChange > 0 && firstAutoLoadData) {//获取到更多数据
                            if (countChange >= onePageTotalCount) {//还有更过数据
                                type = 0;
                            } else if (countChange < onePageTotalCount) {//没有更多数据
                                type = 1;
                            }
                        } else {//获取第一页数据
                            type = 2;
                        }
                        historyTotalCount = totalCount;
                        //滑动到最后一行
                        if (firstVisibleItem + visibleItemCount > totalCount - 1 && !loadDataNow) {
                            addFooter(type);
                        } else if (footer != null) {
                            footer.setVisibility(View.GONE);
                        }
                    }
                }
            });

以下是自定义全部代码

public class LoadSwipeRefreshLayout extends SwipeRefreshLayout {
    private final String TAG = "tag_" + LoadSwipeRefreshLayout.this.getClass().getSimpleName();
    private ListView listView = null;
    private View footer;
    private TextView footerText;
    private ProgressBar footerProgress;
    /**
     * 记录是否移除 footerView 防止上拉刷新时第一页手动加载失效
     */
    private boolean removeFooter = false;
    //是否正在处理加载数据回调,正在加载数据时不再回调加载数据
    private boolean loadDataNow = false;

    /**
     * 第二页是否自动加载更多数据
     * firstAutoLoadData true 自动加载 false 点击加载
     */
    private boolean firstAutoLoadData = true;
    /**
     * 底部 footerView 显示状态
     * 0 正在加载 1 没有更多数据 2 点击加载更多数据
     */
    private int type = 0;
    //每页数据条数
    private int onePageTotalCount = 9;
    //历史条数
    private int historyTotalCount = 0;
    //状态回调
    private IRefreshCallBack refreshCallBack;

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

    public LoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    public void setFirstAutoLoadData(boolean firstAutoLoadData) {
        this.firstAutoLoadData = firstAutoLoadData;
    }

    public void setOnePageTotalCount(int onePageTotalCount) {
        this.onePageTotalCount = onePageTotalCount;
    }

    public void setRefreshCallBack(IRefreshCallBack refreshCallBack) {
        this.refreshCallBack = refreshCallBack;
    }

    /**
     * 初始化获取到 listView 控件
     */
    private void init() {
        log("init()");
        int childCount = getChildCount();
        log(childCount);
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof ListView) {
                listView = (ListView) childView;
                break;
            }
        }
        initFooter();
        if (listView != null) {
            listView.setOnScrollListener(new AbsListView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(AbsListView absListView, int i) {
                }

                /**
                 * 滑动监听
                 * @param absListView listView 的父类
                 * @param firstVisibleItem 屏幕可见第一个 item 位置(不一定是 0)
                 * @param visibleItemCount 屏幕可见的最后一个 item 与 firstVisibleItem 相隔多少个 item
                 * @param totalCount listView 的 item 总数包括添加的 headView、footerView
                 */
                @Override
                public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalCount) {
//                    log("onScroll firstVisibleItem = " + firstVisibleItem + "visibleItemCount = " + visibleItemCount + "totalCount = " + totalCount);
                    //数据不在加载中
                    int countChange = totalCount - historyTotalCount;
                    if (!loadDataNow && countChange != 0) {
                        //如果是第一页刷新后加载数据回调加载数据接口
                        if (removeFooter && countChange == 1) {
                            removeFooter = false;
                            return;
                        }
                        //有新数据加载
                        log("countChange= " + countChange);
                        if (countChange > 0 && firstAutoLoadData) {//获取到更多数据
                            if (countChange >= onePageTotalCount) {//还有更过数据
                                type = 0;
                            } else if (countChange < onePageTotalCount) {//没有更多数据
                                type = 1;
                            }
                        } else {//获取第一页数据
                            type = 2;
                        }
                        historyTotalCount = totalCount;
                        //滑动到最后一行
                        if (firstVisibleItem + visibleItemCount > totalCount - 1 && !loadDataNow) {
                            addFooter(type);
                        } else if (footer != null) {
                            footer.setVisibility(View.GONE);
                        }
                    }
                }
            });
        }
    }

    /**
     * 初始化 footerView
     */
    private boolean initFooter() {
        if (footer == null) {
            int footerViewsCount = listView.getFooterViewsCount();
            log("footerViewsCount = " + footerViewsCount);
            if (footerViewsCount == 0) {
                LayoutInflater inflater = LayoutInflater.from(getContext());
                footer = inflater.inflate(R.layout.list_footer, null);
                listView.addFooterView(footer);
                footerProgress = footer.findViewById(R.id.footer_progress_bar);
                footerText = footer.findViewById(R.id.footer_content);
                footerText.setText(R.string.click_load_update);
                footer.setVisibility(View.GONE);
            }
            footerText.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (type == 2) {
                        loadDataCallBack();
                    }
                }
            });
        }
        return footer != null;
    }

    /**
     * 显示底部footerView
     *
     * @param type 0 立即加载 1 没有更多数据 2 点击加载更多数据
     */
    private void addFooter(@IntRange(from = 0, to = 2) final int type) {
        log("addFooter = type = " + type);
        loadDataNow = true;
        firstAutoLoadData = true;
        if (initFooter())
            footer.setVisibility(VISIBLE);
        if (listView != null) {
            switch (type) {
                case 1:
                case 2:
                    footerText.setText(type == 1 ? R.string.no_more_url_date : R.string.click_load_update);
                    footerProgress.setVisibility(View.GONE);
                    break;
            }
            if (type != 2) {
                loadDataCallBack();
            }
        }
    }

    /**
     * 没有更多数据
     */
    public void noMoreData() {
        addFooter(1);
    }

    /**
     * 加载数据回调
     */
    private void loadDataCallBack() {
        if (refreshCallBack != null) {
            footerText.setText(R.string.load_now);
            footerProgress.setVisibility(View.VISIBLE);
            refreshCallBack.refreshCallBack(LoadSwipeRefreshLayout.this);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //onLayout 方法会被多次调用,但初始化只要执行一次就好
        if (listView == null && footer == null)
            init();
    }

    /**
     * 隐藏底部 footerView
     */
    public void hideFooterView() {
        if (footer != null)
            footer.setVisibility(GONE);
        loadDataNow = false;//释放加载数据的状态
    }

    /**
     * 上拉刷新移除 footerView 否则有时候会报数组越界异常
     */
    public void removeFooterView() {
        listView.removeFooterView(footer);
        footer = null;
        removeFooter = true;
    }

    /**
     * 打印日志
     *
     * @param s 打印内容
     */
    private void log(String s) {
        Log.e(TAG, s);
    }

    private void log(int s) {
        Log.e(TAG, String.valueOf(s));
    }
}

activity 调用代码(使用的 google 推荐的官方编写 android 的语言 kotlin )

Kotlin 第一个 APP

class MainActivity : AppCompatActivity(),Thread.UncaughtExceptionHandler  {
    override fun uncaughtException(p0: Thread?, p1: Throwable?) {
        if(listData != null){
            Log.e("tag_MainActivity","listData.size= ${listData.size}")
        }
        finish()
    }
    var listData = ArrayList<String>()
    var page = 1
    var adapter : OnTextAdapter? = null
    var handler : Handler? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.e("tag_fun", "onCreate")
        setContentView(R.layout.activity_main)
        content.text = "你好"
        handler = Handler(mainLooper, Handler.Callback {
            Log.e("tag_system_time_handler", "load_data${System.currentTimeMillis()/1000}thread.name= ${Thread.currentThread().name}")
            swipeRefresh.isRefreshing = false
            swipeRefresh.hideFooterView()
            if(adapter == null){
                adapter = OnTextAdapter(context = this@MainActivity, listDate = listData)
                list.adapter = adapter
            }else{
                adapter?.notifyDataSetChanged()
                Log.e("tag_MainActivity","adapter.notifyDataSetChanged()")
            }

            false
        })
        swipeRefresh.setFirstAutoLoadData(false)
        swipeRefresh.isRefreshing = true
        addData(page)
        swipeRefresh.setRefreshCallBack {
            addData(++ page)
        }
        swipeRefresh.setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener {
            page = 1
            listData.removeAll(listData)
            swipeRefresh.removeFooterView()
            addData(page)
        })
    }
    private fun addData(page : Int) {
        Log.e("tag_addData", "addData(page= $page)")
        Thread {
            Log.e("tag_addData", "Thread.name=${Thread.currentThread().name}")
            for (i in (page - 1) * 10 + 1 .. page * 10){
                listData.add("text_$i")
            }
            handler?.sendEmptyMessageDelayed(1, 1 * 1000)
        }.start()
    }

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值