1. 程式人生 > >Android上拉載入更多ListView——PulmListView

Android上拉載入更多ListView——PulmListView

宣告

本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出

思路

今天帶大家實現一個上拉載入更多的ListView.GitHub傳送門:PulmListView, 歡迎大家fork&&star.

先帶大家看一下示例效果:
效果圖

然後帶大家理一下實現思路, 如果我們要實現一個上拉載入更多的ListView, 我們需要實現的功能包括:

  1. 一個自定義的ListView, 並且該ListView能夠判斷當前是否已經處於最底部.
  2. 一個自定義的FooterView, 用於在ListView載入更多的過程中進行UI展示.
  3. 關聯FooterView和ListView, 包括載入時機判斷、FooterView的顯示和隱藏.
  4. 提供一個載入更多的介面, 便於回撥使用者真正載入更多的功能實現.
  5. 提供一個載入更多結束的回撥方法, 用於新增使用者的最新資料並更新相關狀態標記和UI顯示.

針對上面的5個功能, 我們挨個分析對應的實現方法.

功能1(自定義ListView)

我們可以通過繼承ListView, 實現一個自定義的PulmListView.

public class PulmListView extends ListView {
    public PulmListView(Context context) {
        this(context, null);
    }

    public
PulmListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PulmListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 初始化 init(); } }

只是實現ListView的三個建構函式還不夠, 我們需要ListView能夠判斷當前的ListView是否滑動到最後一個元素.

判斷是否滑動到最後一個元素, 我們可以通過為ListView設定OnScrollListener來實現.程式碼如下:

private void init() {
    super.setOnScrollListener(new OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            // 呼叫使用者設定的OnScrollListener
            if (mUserOnScrollListener != null) {
                mUserOnScrollListener.onScrollStateChanged(view, scrollState);
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            // 呼叫使用者設定的OnScrollListener
            if (mUserOnScrollListener != null) {
                mUserOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
            }

            // firstVisibleItem是當前螢幕能顯示的第一個元素的位置
            // visibleItemCount是當前螢幕能顯示的元素的個數
            // totalItemCount是ListView包含的元素總數
            int lastVisibleItem = firstVisibleItem + visibleItemCount;
            if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) {
                if (mOnPullUpLoadMoreListener != null) {
                    mIsLoading = true;
                    mOnPullUpLoadMoreListener.onPullUpLoadMore();
                }
            }
        }
    });
}

從程式碼註釋可以知道, 通過(firstVisibleItem + visibleItemCount)可以獲取當前螢幕已經展示的元素個數, 如果已經展示的元素個數等於ListView的元素總數, 則此時可以認為ListView已經滑動到底部.

功能2(自定義的FooterView)

這裡我們可以實現一個比較簡單的FooterView, 即載入更多的UI佈局.例如我們可以展示一個ProgressBar和一行文字, 具體程式碼如下:

/**
 * 載入更多的View佈局,可自定義.
 */
public class LoadMoreView extends LinearLayout {

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

    public LoadMoreView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadMoreView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        LayoutInflater.from(getContext()).inflate(R.layout.lv_load_more, this);
    }
}

佈局檔案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/id_load_more_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center"
    android:layout_margin="@dimen/loading_view_margin_layout">

    <ProgressBar
        android:id="@+id/id_loading_progressbar"
        android:layout_width="@dimen/loading_view_progress_size"
        android:layout_height="@dimen/loading_view_progress_size"
        android:indeterminate="true"
        style="?android:progressBarStyleSmall"/>

    <TextView
        android:id="@+id/id_loading_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/page_loading"/>
</LinearLayout>

功能3(關聯ListView和FooterView)

第一,我們需要在ListView中通過一個變數儲存FooterView, 並且在建構函式中將其例項化.

private View mLoadMoreView;
private void init() {
    mLoadMoreView = new LoadMoreView(getContext());
}

第二,我們需要控制FooterView的顯示和隱藏.考慮一下FooterView的顯示和隱藏的時機:

  • 顯示的時機: ListView處於最底部並且當前還有更多的資料需要載入.
  • 隱藏的時機: ListView結束完載入更多的操作.

為了判斷當前是否還有資料需要載入, 因此我們需要定義一個boolean變數mIsPageFinished, 表示資料載入是否結束.
為了保證同一時間只進行一次資料載入過程, 因此我們還需要定義一個boolean變數mIsLoading, 表示當前是否已經處於資料載入狀態.

明確了FooterView的顯示和隱藏時機, 也有了控制狀態的變數, 程式碼也就比較容易實現了.

顯示時機:

private void init() {
    mIsLoading = false; // 初始化時沒處於載入狀態
    mIsPageFinished = false; // 初始化時預設還有更多資料需要載入
    mLoadMoreView = new LoadMoreView(getContext()); // 例項化FooterView
    super.setOnScrollListener(new OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            // 呼叫使用者設定的OnScrollListener
            if (mUserOnScrollListener != null) {
                mUserOnScrollListener.onScrollStateChanged(view, scrollState);
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            // 呼叫使用者設定的OnScrollListener
            if (mUserOnScrollListener != null) {
                mUserOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
            }

            int lastVisibleItem = firstVisibleItem + visibleItemCount;
            // 當處於ListView尾部且有更多資料需要載入且當前沒有載入程式再進行中時, 執行載入更多操作
            if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) {
                if (mOnPullUpLoadMoreListener != null) {
                    mIsLoading = true; // 將載入更多進行時狀態設定為true
                    showLoadMoreView(); // 顯示載入更多佈局
                    mOnPullUpLoadMoreListener.onPullUpLoadMore(); // 呼叫使用者設定的載入更多回調介面
                }
            }
        }
    });
}

private void showLoadMoreView() {
    // 這裡將載入更多的根佈局id設定為id_load_more_layout, 便於使用者自定製載入更多佈局.
    if (findViewById(R.id.id_load_more_layout) == null) {
        addFooterView(mLoadMoreView);
    }
}

隱藏時機:

/**
 * 載入更多結束後ListView回撥方法.
 *
 * @param isPageFinished 分頁是否結束
 * @param newItems       分頁載入的資料
 * @param isFirstLoad    是否第一次載入資料(用於配置下拉重新整理框架使用, 避免出現頁面閃現)
 */
public void onFinishLoading(boolean isPageFinished, List<?> newItems, boolean isFirstLoad) {
    mIsLoading = false; // 標記當前已經沒有載入更多的程式在執行
    setIsPageFinished(isPageFinished); // 設定分頁是否結束標誌並移除FooterView
}

private void setIsPageFinished(boolean isPageFinished) {
    mIsPageFinished = isPageFinished;
    removeFooterView(mLoadMoreView);
}

功能4(上拉載入更多實現的回撥介面)

這個比較簡單, 我們定義一個interface, 便於回撥使用者真正的載入更多的實現方法.

/**
 * 上拉載入更多的回撥介面
 */
public interface OnPullUpLoadMoreListener {
    void onPullUpLoadMore();
}

private OnPullUpLoadMoreListener mOnPullUpLoadMoreListener;
/**
 * 設定上拉載入更多的回撥介面.
 * @param l 上拉載入更多的回撥介面
 */
public void setOnPullUpLoadMoreListener(OnPullUpLoadMoreListener l) {
    this.mOnPullUpLoadMoreListener = l;
}

功能5(載入更多的結束回撥)

為了在PulmListView中維護資料集合, 必須自定義一個Adapter, 在Adapter中使用List儲存資料集合, 並提交增刪的方法.

自定義的Adapter:

/**
 * 抽象的Adapter.
 */
public abstract class PulmBaseAdapter<T> extends BaseAdapter {
    protected List<T> items;

    public PulmBaseAdapter() {
        this.items = new ArrayList<>();
    }

    public PulmBaseAdapter(List<T> items) {
        this.items = items;
    }

    public void addMoreItems(List<T> newItems, boolean isFirstLoad) {
        if (isFirstLoad) {
            this.items.clear();
        }
        this.items.addAll(newItems);
        notifyDataSetChanged();
    }

    public void removeAllItems() {
        this.items.clear();
        notifyDataSetChanged();
    }
}

為什麼在addMoreItems方法中要增加一個isFirstLoad變數呢?

是因為上拉載入更多通常要配合下拉重新整理使用.而下拉重新整理的過程中會牽扯到ListView的資料集合clear然後再addAll.如果沒有isFirstLoad引數, 那使用者下拉重新整理去更新ListView的資料集合就必須分為兩步:

  1. removeAllItems並進行notifyDataSetChanged.
  2. addMoreItems並進行notifyDataSetChanged.

同一時間連續兩次notifyDataSetChanged會導致螢幕閃屏, 因此這裡提交了一個isFirstLoad方法.當是第一次載入資料時, 會先clear掉所有的資料, 然後再addAll, 最後再notify.

有了自定義的adapter, 就可以寫載入更多結束的回撥函數了:

/**
 * 載入更多結束後ListView回撥方法.
 *
 * @param isPageFinished 分頁是否結束
 * @param newItems       分頁載入的資料
 * @param isFirstLoad    是否第一次載入資料(用於配置下拉重新整理框架使用, 避免出現頁面閃現)
 */
public void onFinishLoading(boolean isPageFinished, List<?> newItems, boolean isFirstLoad) {
    mIsLoading = false;
    setIsPageFinished(isPageFinished);
    // 新增更新後的資料
    if (newItems != null && newItems.size() > 0) {
        PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();
        adapter.addMoreItems(newItems, isFirstLoad);
    }
}

這裡需要注意, 當添加了FooterView或者HeaderView之後, 我們無法通過listview.getAdapter拿到我們自定義的adapter, 必須按照如下步驟:

PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();

參考

相關推薦

Android載入ListView——PulmListView

宣告 本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 思路 今天帶大家實現一個上拉載入更多的ListView.GitHub傳送門:PulmListView, 歡迎大家fork&&star. 先帶大家看一下示例效果:

AndroidListView重新整理載入效果實現

  在Android開發中,下拉重新整理和上拉載入更多在很多app中都會有用到,下面就是具體的實現的方法。 首先,我們自定義一個RefreshListView來繼承與ListView,下面是程式碼: package com.example.downrefresh; import

Android ListView重新整理,載入,帶動畫 自定義控制元件

之前每次 專案中用到ListView 的 下拉重新整理 以及上拉分頁載入 都是 用的 網上 下載 的 類庫, 使用起來 諸多不便 ,於是 趁著有空 ,自己封裝了ListView 讓其 實現 下拉重新整理,以及分頁載入功能。 以下是 效果圖: 當 滑動到 ListView 頂

Android分組列表懸停顯示,分組listView懸停效果,帶下重新整理和載入

分組列表,帶下拉重新整理和上拉載入更多【專案地址在文章最後!!】 效果圖: 實現過程,借鑑PinnedHeadListView,但是該demo沒有下拉重新整理功能,故將該控制元件整合到PullToRefresh 庫中,【PullToRefresh 庫為第

Android scrollview中巢狀listview實現listview的下重新整理載入

我們都知道在Android中scrollview和listview都能滑動,如果scrollview巢狀listview會出現一些問題,比如listview不能正常顯示item...但是在一些專案中,一些頁面內容比較多,需要在外面放一個scrollview,裡面還會巢狀li

Android滑動衝突解決方式(下重新整理載入,適配RecyclerView/ListView/ScrollView)

@Override public boolean judgeIntercept(float curInterceptY, float lastInterceptY, boolean isHeaderShow, boolean isFooterShow, boolean allowLoadM

androidListView重新整理載入(PullToRefresh框架抽取)

大家不難發現當你使用SwipeRefreshLayout下拉的時候佈局檔案不會跟著手勢往下滑,而且想要更改這個缺陷好像非常不容易。 雖然SwipeRefreshLayout非常簡單易懂,但是需求需要下拉重新整理的時候跟著手勢下滑就不能用SwipeRefreshLayo

android重新整理,載入

public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerview; private ArrayList<Integer> mList = new ArrayL

Android Support V4中的SwipeRefreshLayout支援載入

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android 列表下重新整理載入分頁功能

手機app 列表頁很常用,當資料特別多的時候,為了更好地使用者體驗,需要進行分頁處理。那麼分頁功能怎麼做呢? 看如下核心程式碼 if (mPage == 1 && mList != null) { mList.clear();

自定義ListView重新整理載入功能

本篇的自定義listview包含下拉重新整理和上拉載入更多都是自定義。如果你想把重新整理的圖片做的更炫只需要更換下圖片加上適當的動畫就OK咯!由於沒有合適的圖片就用了個粗糙的。不好看請見諒。 //部分程式碼(都做了註釋): /** * @author: ZQF_

RecycleView的封裝實現載入,可以在有多種RecycleView的佈局,特別有listview存在時使用。

在我們開發app的時候,列表元件總是最常用的。目前下拉重新整理和上拉載入的元件有很多。Github一搜索,大部分的開源專案都只實現了下拉重新整理而沒有上拉載入,也有部分專案把上拉載入更多實現了,但是這樣做其實並不好,因為在app實際的執行中當戶滑動到底部就應該自動載入下一頁的內容(決大部分app都是這樣做的

載入ListView實現

在Android的應用程式中,使用列表來展示內容的應用是最多的。當然,我們從早期的ListView到目前的RecyclerView,列表控制元件的實現更加的優秀。但無論怎樣,我們都會在使用列表控制元件的時候新增下拉重新整理和上拉載入更多。這的效果有多種實現當然也

快速整合Android實現下重新整理載入

本文實現一分鐘快速整合BGARefreshLayout下拉重新整理和上拉載入的功能. 這個是簡單單功能的整合方法,詳細BGARefreshLayout請到github中的demo,這只是我簡化版快速整合. 重要的程式碼實現如下: MainActivity類: pub

(Android)五分鐘讓你輕鬆學會下重新整理和載入

分享一個谷歌自帶的下拉重新整理和上拉載入更多例子: 先看效果圖: /** * 繼承自SwipeRefreshLayout,從而實現滑動到底部時上拉載入更多的功能. */ public class RefreshLayout extends SwipeRefreshL

ListView,GridView和ScrollVIew巢狀實現載入

這個問題找了好幾天,網上沒有直接的答案,今天寫在這裡希望能幫到人,剛寫部落格,樣式什麼的就不看了,簡單易懂好貼上才是你們需要的! 不對的地方請在評論指正,謝謝! 首先說下ListView,GridView和ScrollVIew巢狀問題,自定義一個ListView或GridV

Android使用RecyclerView實現載入,下重新整理,分組顯示

專案地址:點選開啟連結(https://github.com/MrGaoGang/luckly_recyclerview) 使用RecyclerView封裝headerview,footerView,並實現上拉載入更多,下拉重新整理,分組功能(新增上拉載入和下拉

react-native-page-listview使用方法(自定義FlatList/ListView重新整理,載入,方便的實現分頁)

react-native-page-listview 對ListView/FlatList的封裝,可以很方便的分頁載入網路資料,還支援自定義下拉重新整理View和上拉載入更多的View.相容高版本FlatList和低版本ListVIew.元件會根據你使用的re

ReactNative ListView + 載入 + 下重新整理

ListView + 上拉載入更多 + 下拉重新整理 一、內容簡介 ListView列表在添加了上拉載入更多功能之後再新增下拉重新整理 二、程式碼實現 1、引入原生元件 RefreshControl import { ListView, V

Android 自定義ScrollView 支援慣性滑動,慣性回彈效果。支援載入

先講下原理: ScrollView的子View 主要分為3部分:head頭部,滾動內容,fooder底部 我們實現慣性滑動,以及回彈,都是靠超過head或者fooder 就重新滾動到  ,內容的頂部或者底部。 之前看了Pulltorefresh 他是通過不斷改變 head或