Android ListView实现上拉到底部的时候自动刷新数据

来源:互联网 发布:ubuntu开机花屏 编辑:程序博客网 时间:2024/04/26 07:26

        在最近的项目中,为了提高用户的体验,需要实现ListView在滑动到底部的时候进行数据的自动加载,当看到这个需求的时候,我的第一个想法是ListView不是有HeadView和FooterView么,就可以直接拿来用了,最终也的确是用的这个方法,但是在实现的过程中,遇到了很多坑。

        首先,先简单写下ListView的FooterView,就是一个简单的一个进度条加上一个文本提示语句。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.    android:id="@+id/litview_footview"
  4.    android:layout_width="match_parent"
  5.    android:layout_height="match_parent"
  6.    android:gravity="center"
  7.    android:orientation="horizontal"
  8.    >
  9.    <ProgressBar
  10.        android:id="@+id/listview_footview_progressBar"
  11.        style="@android:style/Widget.ProgressBar.Small"
  12.        android:layout_width="wrap_content"
  13.        android:layout_height="wrap_content"
  14.        android:layout_gravity="center" />
  15.    <TextView
  16.        android:id="@+id/listview_footview_textview"
  17.        android:layout_width="wrap_content"
  18.        android:layout_height="wrap_content"
  19.        android:layout_centerInParent="true"
  20.        android:layout_gravity="center"
  21.        android:layout_toRightOf="@+id/listview_footview_progressBar"
  22.        android:paddingBottom="10dp"
  23.        android:paddingLeft="10dp"
  24.        android:paddingTop="10dp"
  25.        android:text="正在加载" />
  26. </LinearLayout>

        然后我们在初始化适配器和ListView本身的时候来将这个footerView加入到ListView中去。有人说需要在绑定适配器之前将FooterView和HeaderView加到ListView中去,然而我试了一下,感觉并没有什么关系的,不知道还有什么猫腻。如果谁知道还请告诉我哦,哈哈。

  1. private void addListViewFooterView() {
  2.        footer = getActivity().getLayoutInflater().inflate(R.layout.listview_footerview, null);
  3.        footProgressBar = (ProgressBar) footer.findViewById(R.id.listview_footview_progressBar);
  4.        footTextView = (TextView) footer.findViewById(R.id.listview_footview_textview);
  5.        listviewMine.addFooterView(footer);
  6.        footTextView.setOnClickListener(new View.OnClickListener() {
  7.            @Override
  8.            public void onClick(View v) {
  9.                if (footTextView.getText().equals(R.string.load_error)) {                    //加载数据
  10.                    getMoreData();
  11.                    footTextView.setText(R.string.loading);
  12.                    footProgressBar.setVisibility(View.VISIBLE);
  13.                }
  14.            }
  15.        });
  16.    }

        在上面的代码中,我将footerView引入进来,然后将里面的控件也取出来,因为后面要对两个控件进行操作,最后将布局设置成ListView的footerView,最后对TextView进行事件的监听,因为在加载错误的时候用户可以点击重新加载数据,当然这个不是重点。

        然后现在需要对ListView的滑动事件进行监听了,当滚到最后一条数据的时候就要自动加载数据了。

  1. listviewMine.setOnScrollListener(new AbsListView.OnScrollListener() {
  2.            @Override
  3.            public void onScrollStateChanged(AbsListView view, int scrollState) {
  4.                // 当不滚动时
  5.                if (scrollState == SCROLL_STATE_IDLE) {
  6.                    //判断是否滚动到底部
  7.                    if (!isLoading && view.getLastVisiblePosition() == view.getCount() - 1) {
  8.                        isLoading = true;                        getMoreData();
  9.                        Log.d("Rollr_Mine", "请求数据...");
  10.                        return;
  11.                    }
  12.                }
  13.            }
  14.            @Override
  15.            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  16. //                if (visibleItemCount == totalItemCount) {
  17. //                    //此时说明当前ListView所有的条目比较少,不足一屏
  18. //                    litviewFootview.setVisibility(View.GONE);
  19. //                } else if (!isLoading&&(firstVisibleItem + visibleItemCount >= totalItemCount)
  20. //                        &&totalItemCount!=0) {
  21. //                    //当第一个可见的条目位置加上当前也所有可见的条目数 等于 ListView当前总的条目数时,就说明已经滑动到了底部,这时候就要去显示FooterView。
  22. //                    isLoading = true;
  23. //                    getMoreData(BaseApp.getInstance().getUserInfo().getUserId(), mCursor);
  24. //                    Log.d("Rollr_Mine","请求数据...");
  25. //                    litviewFootview.setVisibility(View.VISIBLE);
  26. //                }
  27.            }
  28.        });
  29.    }

        上面的代码中就有一个大坑,首先,我们要选择到底是在哪儿来进行操作,有两个方法,一个是

onScrollStateChanged()方法,这个方法是滑动停止的时候才会触发,还有一个方法是onScroll(),这个方法会一直触发,从上面的注释可以看到,当时我是在这填坑了的,最后我还是决定用上面的方法,就是那个滚动停止的时候触发的方法,我们来看一下,假如我们使用下面的方法来实现,这样当滑动到底部的时候,当快要滑动到底部的时候,加载数据的代码会执行,此时,isLoading为true,然后当数据量很少的时候,网络很快的时候,加载数据的代码会很快得到数据,将isLoading的值设成false,此时滑动还没有结束,又满足了所有的条件,又会继续请求数据,所以就会造成在一瞬间,这个模块请求了十几次接口的数据,很明显,这样是不对的,不管是对客户端还是服务器都是不可原谅的,当然有些人会说,只要判断条件再给明确一点就可以避免这个错误,那是当然,我只是觉得这样不好而已。

        然后选择好方法后,我们开始前进了,当滑动到底部,看见了footerView后,停止滑动的时候,满足条件去请求数据了,然后下面是请求数据的代码。

  1. private void getMoreData() {
  2.        getFeedsService().getTopicByUserId().enqueue(new Callback<ResultModel<List<TopicModel>>>() {
  3.            @Override
  4.            public void onResponse(Response<ResultModel<List<TopicModel>>> response) {
  5.                if (response.isSuccess()) {
  6.                    if (response.body().getData() != null) {
  7.                        if (response.body().getData().isEmpty()) {
  8.                            footTextView.setText(R.string.no_more_data);
  9.                            footProgressBar.setVisibility(View.GONE);
  10.                        } else {
  11.                            list.addAll(response.body().getData());
  12.                            isLoading = false;
  13.                        }
  14.                    }
  15.                    adapter.notifyDataSetChanged();
  16.                } else {
  17.                    try {
  18.                        footTextView.setText(R.string.load_error);
  19.                        footProgressBar.setVisibility(View.GONE);
  20.                        Toast.makeText(getActivity(), StringUtils.getJsonString(response.errorBody().string(), "message"), Toast.LENGTH_SHORT).show();
  21.                    } catch (IOException e) {
  22.                        e.printStackTrace();
  23.                    }
  24.                }
  25.            }
  26.            @Override
  27.            public void onFailure(Throwable t) {
  28.                footTextView.setText(R.string.load_error);
  29.                footProgressBar.setVisibility(View.GONE);
  30.            }
  31.        });
  32.    }

        当我们请求到数据的时候,我们会先去是否能够取到数据,如果不能取到数据,做对应的处理,并且将提示放在footerView的TextView里面,比如我的就是如果出错,就在footerView的TextView上显示,加载出错,重新加载,并且要将ProgressBar隐藏掉,然后加载成功的话也分情况,如果在加载成功的前提下,有数据的话不做任何处理,如果返回的数据为空,代表已经没有更多数据加载了。

        在之前的时候我还在考虑在加载完毕的时候将footerView隐藏掉,等到再滑到底部的时候再显示出来,其实这个想着觉得是挺好的,做起来体验并不好,为什么?一来,布局的隐藏是个蛋疼的事情,不过也有解决办法,一来你可以想到removeFooterView,等到需要的时候,再addFooterView,不过你自己使用的时候才知道并没有卵用,如果remove掉了,你再加是加不上的,然后你可能会想到,我通过setVisibility(View.GONE);来隐藏,需要的时候再显示,你试过就会知道这样做会有一个白色的空白,超级难看,当然,这个我也可以帮你解决,用下面的方式你可以做到。而且还没有后遗症,好,你觉得一切都可以了,试一试呗。

  1. //显示
  2. footerView.setVisibility(View.VISIBLE);
  3. footerView.setPadding(0, 0, 0, 0);
  4. //隐藏
  5. footerView.setVisibility(View.GONE);
  6. footerView.setPadding(0, -footerView.getHeight(), 0, 0);

        当试过后你就会发现,当你滑动到底部的时候,如果滑动操作还没有结束,不会执行操作,意思就是那个显示正在加载的那个footerView不会显示,当稳定后,方法触发,那个footerView才会显示,也就是说,你滑动啊滑动,滑到底部的时候啥都没有,然后你松开手指,突然蹦出一个正在加载的View,反正是我就骂娘了,吓死宝宝了。

        说了这么多,就是想说,其实那个footerView没必要隐藏和显示,就一直放那,滑动底部的时候就能看到,正在加载,加载成功了,数据取到了,刷新适配器,那个footerView就会被挤走,你就看不到了,也不会那么突兀。还可以作为一个指示器,也挺好的。

        最后总结一下用法:

        第一步、书写footerView,自定义的

        第二步、绑定适配器并将footerV添加到ListView中去

        第三步、给ListView添加滚动时间监听 就按上面的方法,当看到最后一项并且不再滑动的时候请求数据,当请求数据出错的时候,做不同的处理,并且改变footerView中TextView的文字显示,并根据情况显示还是隐藏ProgressBar,这些看情况吧。

        好了,也不知道说清楚没有,这个也没必要给连接了,呵呵。

        最后吐槽一下,现在的博客抄袭真的是,日了狗了,原创越来越少了,有时候搜点东西吧,一搜好几页的帖子,一打开,发现内容都一样,唉,真的,抄人家的有什么用啊。

        好了,不早了,宝宝休息了。

        -----------更新------------

        我也是很讨厌那种光说不练的人,说一大堆,还不如代码来的实在,所以我抽空写了个demo。。不过万事看需求。。这个不一定适合你,你需要自己缝缝补补才会更适合你。。

        github地址:https://github.com/MZCretin/AutoRefreshListView

0 0
原创粉丝点击