相信很多朋友在开发中都会遇到分页加载的需求,为了满足这个需求,不管是使用Listview或者RecyclerView都要一个加载更多的功能,很多朋友会发现网上下拉刷新的实现一搜一大把,为什么加载更多相对较少呢,是因为加载更多功能相对简单,最主要的原因是加载更多的功能严格来讲并不能作为一个控件的功能去实现,而应该与业务逻辑更为密切一点。
下面的内容为大家讲解ListView中的加载更多功能的实现,RecyclerView中的实现我们后面博客再讲。
首先在ListView中实现这个功能,我们首先想到的利用ListView的FooterView,在不同状态时显示或者隐藏FooterView。
ok,我们想做这样一个功能,当ListView滑动到最底部时自动触发加载更多。ListView的父类AbsListView中有一个方法:
public void setOnScrollListener(OnScrollListener l) {
mOnScrollListener = l;
invokeOnItemScrollListener();
}
滑动监听,ListView中当然有这个方法,简单了有了这个监听我们就可以监听到ListView滑动的状态,实现这个监听:
private OnScrollListener mOnScrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
};
我们可以在这里搞事情,下面就简单了:
private boolean needLoad(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int lastVisibleItem = firstVisibleItem + visibleItemCount;
boolean isAtListEnd = lastVisibleItem == totalItemCount;
return !mIsPullUpLoading && isAtListEnd;
}
这里比较关键判断是否需要显示加载中的View。
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Start a new loading when the last item scrolls into screen, instead of overriding method onTouchEvent.
if (needLoad(firstVisibleItem, visibleItemCount, totalItemCount)&&!hasNoMoreItem) {
startPullUpLoad();
L.d(" need scroll");
}
}
接下来的工作就是提供一系列的接口来控制FooterView的状态以及显示和隐藏。贴出ListView的完整代码。
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
/**
* Created by qiaopeng on 16/11/23.
*/
public class LoadMoreInterceptListView extends ListView {
public LoadMoreInterceptListView(Context context) {
this(context, null);
}
public LoadMoreInterceptListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadMoreInterceptListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private boolean hasNoMoreItem = false;
public interface OnPullUpLoadListener {
void onPullUpLoading();
}
public void setOnPullUpLoadListener(OnPullUpLoadListener listener) {
mOnPullUpLoadListener = listener;
}
// When loading finished, the controller should call this public method to update footer view.
public void onPullUpLoadFinished(boolean hasNoMoreItems) {
// Clear flag
mIsPullUpLoading = false;
hasNoMoreItem = hasNoMoreItems;
if (hasNoMoreItems) {
// when have no more items, update footer view to: NO MORE
mFooterView.updateView(PullUpLoadListViewFooter.State.NOT_LOADING, FOOTER_VIEW_CONTENT_NO_MORE,true);
} else {
// The other cases: (1)Loading succeed and still has more items, (2)Loading failed,
// should hide footer view.
hideFooterView();
}
}
private static final String FOOTER_VIEW_CONTENT_LOADING = "加载中...";
private static final String FOOTER_VIEW_CONTENT_NO_MORE = "已显示全部";
private PullUpLoadListViewFooter mFooterView;
// The flag used to avoid loading again when the list view is already in loading state.
private boolean mIsPullUpLoading;
// The controller should register this listener.
private OnPullUpLoadListener mOnPullUpLoadListener;
private void init() {
mIsPullUpLoading = false;
setOnScrollListener(mOnScrollListener);
}
private OnScrollListener mOnScrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
// do nothing.
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Start a new loading when the last item scrolls into screen, instead of overriding method onTouchEvent.
if (needLoad(firstVisibleItem, visibleItemCount, totalItemCount)&&!hasNoMoreItem) {
startPullUpLoad();
L.d(" need scroll");
}
}
private boolean needLoad(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int lastVisibleItem = firstVisibleItem + visibleItemCount;
boolean isAtListEnd = lastVisibleItem == totalItemCount;
return !mIsPullUpLoading && isAtListEnd;
}
};
private void startPullUpLoad() {
if (mOnPullUpLoadListener != null) {
// Show the foot view and update its state to LOADING.
showFooterView();
// Set flag
mIsPullUpLoading = true;
// Call the callback to notify the listView's hosted controller to load data.
mOnPullUpLoadListener.onPullUpLoading();
}
}
private void showFooterView() {
if (mFooterView == null) {
mFooterView = new PullUpLoadListViewFooter(getContext());
addFooterView(mFooterView);
}
mFooterView.setVisibility(View.VISIBLE);
mFooterView.updateView(PullUpLoadListViewFooter.State.LOADING, FOOTER_VIEW_CONTENT_LOADING,true);
}
// It's better to hide footer instead of removing.
// Since after removing, we should create a new footer instance and add it into view hierarchy again,
// this will call findViewById many times which waste time.
private void hideFooterView() {
if (mFooterView != null) {
mFooterView.setVisibility(View.INVISIBLE);
}
}
}
这是自定义ListView,我们还需要一个类来加载FooterView和控制它的状态。
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.umed.youyueyizu.R;
/**
* Created by qiaopeng on 16/11/23.
*/
public class PullUpLoadListViewFooter extends LinearLayout {
public enum State {
LOADING,
NOT_LOADING,
}
public PullUpLoadListViewFooter(Context context) {
this(context, null);
}
public PullUpLoadListViewFooter(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void updateView(State state, String content,boolean visible) {
if (visible){
if (state == State.LOADING) {
mLoadingLinearLayout.setVisibility(View.VISIBLE);
mNotLoadingLinearLayout.setVisibility(View.INVISIBLE);
mLoadingLabel.setText(content);
} else if (state == State.NOT_LOADING){
mLoadingLinearLayout.setVisibility(View.INVISIBLE);
mNotLoadingLinearLayout.setVisibility(View.VISIBLE);
mNotLoadingLabel.setText(content);
}}else{
mLoadingLinearLayout.setVisibility(View.INVISIBLE);
mNotLoadingLinearLayout.setVisibility(View.INVISIBLE);
mNotLoadingLabel.setText(content);
}
}
private LinearLayout mLoadingLinearLayout;
private TextView mLoadingLabel;
private LinearLayout mNotLoadingLinearLayout;
private TextView mNotLoadingLabel;
// pul in R.id.pul*** means "pull up load"
private void init() {
inflate(getContext(), R.layout.pull_listview_footer, this);
mLoadingLinearLayout = (LinearLayout) findViewById(R.id.pulListViewFooter_loading);
mLoadingLabel = (TextView) findViewById(R.id.pulListViewFooter_loadingLabel);
mNotLoadingLinearLayout = (LinearLayout) findViewById(R.id.pulListViewFooter_notLoading);
mNotLoadingLabel = (TextView) findViewById(R.id.pulListViewFooter_notLoadingLabel);
}
}
基本上完事了,FooterView的视图就自己随意发挥吧,想设计多漂亮就弄多漂亮。