Skip to content

Instantly share code, notes, and snippets.

@jingle1267
Created October 11, 2014 14:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jingle1267/32bf79a2e72ed8f5e5a6 to your computer and use it in GitHub Desktop.
Save jingle1267/32bf79a2e72ed8f5e5a6 to your computer and use it in GitHub Desktop.
ListViewLoadMoreManager
package me.xiaopan.android.widget;
import java.lang.reflect.Field;
import android.content.Context;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* ListView加载更多管理器
* <br>
* <br>首先你需要使用ListViewLoadMoreManager(ListView, OnLoadMoreListener)构造函数来创建一个ListViewLoadMoreManager
* <br>
* <br>然后在加载完数据后调用ListViewLoadMoreManager的setAdapter(ListAdapter, boolean)方法来设置Adapter,第二个参数的意思是是否已全部加载完毕,如果为true将切换到end状态,并且永远不会再触发加载
* <br>
* <br>最后你需要在OnLoadMoreListener的onLoadMore()方法里分页加载更多的数据并根据加载结果调用loadFinished(boolean)或loadFailed()方法来结束加载
* <br>值的注意的是loadFinished(boolean)方法的参数,意思是是否已全部加载完毕,如果为true将切换到end状态,并且永远不会再触发加载
* <br>
* <br>
* <br>另外
* <br>
* <br>如果你想自定义ListFooterView那么你需要自定义一个View并实现LoadMoreListFooter接口,然后在创建ListViewLoadMoreManager的时候使用ListViewLoadMoreManager(ListView, LoadMoreListFooter, OnLoadMoreListener)构造函数并传入你自定义的ListFooterView
* <br>
* <br>默认开启了滚动到底部自动加载的功能,你可以通过setScrollToBottomAutoLoad()方法关闭
* <br>
* <br>由于需要设置OnScrollListener才能实现功能,所以你也要设置OnScrollListener的话就需要调用ListViewLoadMoreManager的setOnScrollListener来设置,如果在之前已经设置了OnScrollListener的话也不用担心,ListViewLoadMoreManager会先拿到已存在的OnScrollListener并回调它
*/
public class ListViewLoadMoreManager implements OnScrollListener{
private static final String NAME = ListViewLoadMoreManager.class.getSimpleName();
private boolean end; // 是否已经结束加载(已经结束的话就不再处理任何事件或操作)
private boolean loading; // 表示是否正在加载(正在加载的时候就不再处理滚动事件)
private boolean allowClickLoad; // 是否允许通过点击Footer触发加载
private boolean debugMode; // 开启DEBUG模式,开启后会在控制台输出追踪LOG
private boolean needRollback; // 表示是否需要回滚
private boolean scrollToBottomAutoLoad; // 滚动到底部自动加载
private View footerView;
private ListView listView;
private OnScrollListener onScrollListener;
private LoadMoreListFooter loadMoreListFooter;
private OnLoadMoreListener onLoadMoreListener;
/**
*
* @param listView 需要添加加载更多功能的列表
* @param footer 列表尾视图
* @param onLoadMoreListener 加载监听器
*/
public ListViewLoadMoreManager(ListView listView, LoadMoreListFooter footer, OnLoadMoreListener onLoadMoreListener) {
this.listView = listView;
this.loadMoreListFooter = footer;
this.footerView = (View) footer;
this.onLoadMoreListener = onLoadMoreListener;
this.footerView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(allowClickLoad){
load();
}
}
});
try {
Field field = ListView.class.getSuperclass().getDeclaredField("mOnScrollListener");
field.setAccessible(true);
Object object = field.get(listView);
if(object != null && object instanceof OnScrollListener){
this.onScrollListener = (OnScrollListener) object;
}
} catch (Exception e) {
e.printStackTrace();
}
this.listView.setOnScrollListener(this);
this.scrollToBottomAutoLoad = true;
}
/**
* 将会采用默认的列表尾视图
* @param listView 需要添加加载更多功能的列表
* @param onLoadMoreListener 加载监听器
*/
public ListViewLoadMoreManager(ListView listView, OnLoadMoreListener onLoadMoreListener){
this(listView, new DefaultLoadMoreListFooterView(listView.getContext(), -1), onLoadMoreListener);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(onScrollListener != null){
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(onScrollListener != null){
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(!scrollToBottomAutoLoad){
if(debugMode){
Log.i(NAME, "onScroll:滚动到底部自动加载功能已关闭,你可以通过setScrollToBottomAutoLoad()方法开启");
}
return;
}
int lastVisibleItem = firstVisibleItem + visibleItemCount; // 当前最后一个显示的Item
// 如果已经结束加载就直接结束不再处理
if(end){
if(debugMode){
Log.i(NAME, "onScroll:已加载完毕:"+lastVisibleItem);
}
return;
}
// 如果正在加载中就直接结束不再处理
if(loading){
if(debugMode){
Log.i(NAME, "onScroll:正在加载中:"+lastVisibleItem);
}
return;
}
// 如果内容尚未充满就直接结束不再处理
if(visibleItemCount == totalItemCount){
if(debugMode){
Log.i(NAME, "onScroll:内容尚未充满:"+lastVisibleItem);
}
return;
}
int triggerItem = totalItemCount - 1;
// 如果需要回滚
if(needRollback){
// 如果当前滚动位置依然大于等于触发位置就直接结束不再处理
if(lastVisibleItem >= triggerItem){
if(debugMode){
Log.d(NAME, "onScroll:需要回滚:"+lastVisibleItem);
}
return;
// 如果已经回滚完毕了就设置不需要回滚
}else{
if(debugMode){
Log.w(NAME, "onScroll:回滚完毕:"+lastVisibleItem);
}
needRollback = false;
}
}
// 如果滚动到了最后一项,就启动加载
if(lastVisibleItem == triggerItem){
if(onLoadMoreListener != null){
load();
}
}
}
/**
* 设置适配器
* @param listAdapter 适配器
* @param end true:已全部加载完毕,将回调LoadMoreListFooter.end()方法显示加载完毕;false:尚未加载完毕,将回调LoadMoreListFooter.clockLoad()方法显示点击加载
*/
public void setAdapter(ListAdapter listAdapter, boolean end){
listView.removeFooterView(footerView);
listView.addFooterView((View) footerView);
listView.setAdapter(listAdapter);
this.end = end;
if(end){
allowClickLoad = false;
loadMoreListFooter.end();
}else{
allowClickLoad = true;
loadMoreListFooter.clickLoad();
}
}
/**
* 开始加载
*/
private void load(){
if(end){
if(debugMode){
Log.i(NAME, "load:已加载完毕");
}
return;
}
if(loading){
if(debugMode){
Log.i(NAME, "load:正在加载中");
}
return;
}
if(debugMode){
Log.w(NAME, "load:开始加载");
}
loading = true;
needRollback = true;
allowClickLoad = false;
loadMoreListFooter.loading();
onLoadMoreListener.onLoadMore(this);
}
/**
* 加载完成
*/
public void loadFinished(boolean end){
if(end){
if(debugMode){
Log.i(NAME, "loadFinished:已加载完毕");
}
return;
}
if(!loading){
if(debugMode){
Log.i(NAME, "loadFinished:尚未加载");
}
return;
}
if(end){
if(debugMode){
Log.w(NAME, "loadFinished:已全部加载完毕");
}
end = true;
loading = false;
allowClickLoad = false;
loadMoreListFooter.end();
}else{
if(debugMode){
Log.w(NAME, "loadFinished:加载完成");
}
loading = false;
allowClickLoad = true;
loadMoreListFooter.clickLoad();
}
}
/**
* 加载失败
*/
public void loadFailed(){
if(end){
if(debugMode){
Log.i(NAME, "loadFailed:已加载完毕");
}
return;
}
if(!loading){
if(debugMode){
Log.i(NAME, "loadFailed:尚未加载");
}
return;
}
if(debugMode){
Log.e(NAME, "loadFailed:加载失败");
}
loading = false;
allowClickLoad = true;
loadMoreListFooter.failed();
}
/**
* 设置滚动监听器
* @param onScrollListener
*/
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
listView.setOnScrollListener(this);
}
/**
* 设置DEBUG模式
* @param debugMode
*/
public void setDebugMode(boolean debugMode) {
this.debugMode = debugMode;
}
/**
* 设置加载更多监听器
* @param onLoadMoreListener
*/
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
/**
* 设置是否滚动到底部自动加载,默认true
* @param scrollToBottomAutoLoad
*/
public void setScrollToBottomAutoLoad(boolean scrollToBottomAutoLoad) {
this.scrollToBottomAutoLoad = scrollToBottomAutoLoad;
}
/**
* 加载更多监听器
*/
public interface OnLoadMoreListener{
/**
* 加载更多
* @param loadMore
*/
public void onLoadMore(ListViewLoadMoreManager loadMore);
}
/**
* 加载更多列表尾接口
*/
public interface LoadMoreListFooter{
/**
* 点击加载
*/
public void clickLoad();
/**
* 加载中
*/
public void loading();
/**
* 加载失败
*/
public void failed();
/**
* 已全部加载完毕
*/
public void end();
}
/**
* 默认的加载更多列表尾视图
*/
public static class DefaultLoadMoreListFooterView extends LinearLayout implements LoadMoreListFooter{
private ProgressBar progressBar;
private TextView textView;
public DefaultLoadMoreListFooterView(Context context, int textColor) {
super(context);
setGravity(Gravity.CENTER);
setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(getContext(), 50)));
int progressWidth = dp2px(getContext(), 30);
progressBar = new ProgressBar(getContext());
addView(progressBar, new LinearLayout.LayoutParams(progressWidth, progressWidth));
textView = new TextView(getContext());
if(textColor > 0){
textView.setTextColor(textColor);
}
LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
textViewParams.leftMargin = dp2px(getContext(), 8);
addView(textView, textViewParams);
}
/**
* dp单位转换为px
* @param context 上下文,需要通过上下文获取到当前屏幕的像素密度
* @param dpValue dp值
* @return px值
*/
private int dp2px(Context context, float dpValue){
return (int)(dpValue * (context.getResources().getDisplayMetrics().density) + 0.5f);
}
@Override
public void loading() {
progressBar.setVisibility(View.VISIBLE);
textView.setText("正在加载更多,请稍后…");
}
@Override
public void failed() {
progressBar.setVisibility(View.GONE);
textView.setText("加载更多失败,点击重新加载");
}
@Override
public void end() {
progressBar.setVisibility(View.GONE);
textView.setText("THE END");
}
@Override
public void clickLoad() {
progressBar.setVisibility(View.GONE);
textView.setText("点击加载更多");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment