Android ListView實現上拉到底部的時候自動刷新數據

        在最近的項目中,爲了提高用戶的體驗,需要實現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

        ----------------------------

        如果您覺得不錯,請打賞我一杯咖啡的錢!

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章