android上拉分页加载更多,Android实现上拉加载更多ListView(PulmListView)

思路

今天带大家实现一个上拉加载更多的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);

}

}

布局文件:

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">

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"/>

android:id="@+id/id_loading_label"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/page_loading"/>

功能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 extends BaseAdapter {

protected List items;

public PulmBaseAdapter() {

this.items = new ArrayList<>();

}

public PulmBaseAdapter(List items) {

this.items = items;

}

public void addMoreItems(List 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();

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值