Created
October 11, 2014 14:47
-
-
Save jingle1267/32bf79a2e72ed8f5e5a6 to your computer and use it in GitHub Desktop.
ListViewLoadMoreManager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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