Android 动态加载 ListView 实现
jopen
11年前
摘要: 如今动态加载成为app 必有的功能,本质上我并不需要一次性加载所有东西,尤其是联网加载时候,前几天看到一篇博文讲国外一个开源解决方案,感觉他的做法并不完美于是发表自己的一些拙见!
首先讲原理:
ListView 可以设置一个滚动监听器
android.widget.AbsListView.setOnScrollListener(OnScrollListener l)有个方法
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)这里面有三个参数:
firstVisibleItem: 第一个可见Item在所有Item中的位置(即屏幕上显示的第一行,在你的数据数组中的位置)
visibleItemCount: 可见Item个数(屏幕内可以显示多少行)
totalItemCount: 总共有多少行数据
通过这个3个参数容易想到,如果 firstVisibleItem + visibleItemCount >= totalItemCount 不就表明 列表已经滑到了底部么?这个时候就是我们加载数据的时机了!
然后我们需要在列表底部增加一个 item 显示:点击加载更多 或者 正在加载中请稍后 或者 没有更多数据了
这个我们要用到
ListView.addFooterView(View v)
添加了 footView ,footView 就成了列表最后一行,也就说相对于你的总数据增加了一行,所有这里有一点要注意的地方:
调用这个方法必须在 ListView.SetAdapter() 之前,否则将会影响 Cursor 类适配器
知道了原理就很简单了,下面奉上我封装的 LoaderListView
/** * 下拉自动加载的 Listview * * @author Yichou * @创建日期 2013-3-19 16:01:10 * * 2013-6-30 */ public class LoaderListView extends ListView implements OnScrollListener, OnItemClickListener, OnClickListener { public interface LoadNotifyer { public void load(); } public interface OnScrollStateChangedListener { public void onScrollStateChanged(int oldState, int newState); } private LinearLayout footViewLoading, footViewRetry, footViewNomore; private LoadNotifyer loadNotifyer; private int scrollState; private OnScrollStateChangedListener onScrollStateChangedListener; public LoaderListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public LoaderListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public LoaderListView(Context context) { super(context); init(context); } @Override public void onClick(View v) { if(v.getId() == 0x1001){ //重新加载 setFootviewType(FOOTVIEW_TYPE.LOADING); if(loadNotifyer != null) loadNotifyer.load(); }else if (v.getId() == 0x1002) { setSelection(0); } } private void init(Context context) { footViewLoading = new LinearLayout(context); footViewLoading.setOrientation(LinearLayout.HORIZONTAL); footViewLoading.setGravity(Gravity.CENTER); ProgressBar bar = new ProgressBar(context); TextView textView = new TextView(context); textView.setText("加载中..."); footViewLoading.addView(bar, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); footViewLoading.addView(textView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); footViewRetry = new LinearLayout(context); footViewRetry.setOrientation(LinearLayout.HORIZONTAL); footViewRetry.setGravity(Gravity.CENTER); textView = new TextView(context); textView.setId(0x1001); textView.setGravity(Gravity.CENTER); textView.setText("网络不给力,请重试 o(︶︿︶)o"); textView.setOnClickListener(this); footViewRetry.addView(textView, new LayoutParams(LayoutParams.WRAP_CONTENT, getFixPx(50))); footViewNomore = new LinearLayout(context); footViewNomore.setOrientation(LinearLayout.HORIZONTAL); footViewNomore.setGravity(Gravity.CENTER); footViewNomore.setId(0x1002); textView = new TextView(context); textView.setText("返回顶部↑"); textView.setGravity(Gravity.CENTER); footViewNomore.setClickable(true); footViewNomore.setOnClickListener(this); footViewNomore.addView(textView, new LayoutParams(LayoutParams.WRAP_CONTENT, getFixPx(50))); setFootviewType(FOOTVIEW_TYPE.LOADING); setOnScrollListener(this); scrollState = SCROLL_STATE_IDLE; super.setOnItemClickListener(this); } public enum FOOTVIEW_TYPE { /** 加载中 */ LOADING, /** 没有更多了,返回顶部 */ NOMOR, /** 加载失败重试 */ RETRY, /**无*/ NONE } private View curFootView; public void setFootviewType(FOOTVIEW_TYPE type) { if(curFootView != null && curFootView.getTag() == type) return; if(curFootView != null) removeFooterView(curFootView); switch (type) { case LOADING: curFootView = footViewLoading; break; case NOMOR: curFootView = footViewNomore; break; case RETRY: curFootView = footViewRetry; break; case NONE: return; } addFooterView(curFootView); curFootView.setTag(type); } private View curHeadView; public void setHeadView(View v) { if(curHeadView!=null) return; curHeadView=v; addHeaderView(v); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState != this.scrollState) { if(onScrollStateChangedListener != null){ onScrollStateChangedListener.onScrollStateChanged(this.scrollState, scrollState); } this.scrollState = scrollState; } } protected int firstVisibleItem, visibleItemCount, totalItemCount; @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(totalItemCount < 2 ) //footview 也算 return; // System.out.println("first=" + firstVisibleItem + ",visible=" + visibleItemCount + ",total=" + totalItemCount); if(firstVisibleItem + visibleItemCount >= totalItemCount){ //说明 footView 可见,通知加载更多 if (loadNotifyer != null && (curFootView != footViewNomore)) { loadNotifyer.load(); } } this.firstVisibleItem = firstVisibleItem; this.visibleItemCount = visibleItemCount; this.totalItemCount = totalItemCount; } public int getFirstVisibleItem() { return firstVisibleItem; } public int getVisibleItemCount() { return visibleItemCount; } public int getScrollState() { return scrollState; } public void setLoadNotifyer(LoadNotifyer loadNotifyer) { this.loadNotifyer = loadNotifyer; } public void setOnScrollStateChangedListener(OnScrollStateChangedListener onScrollStateChangedListener) { this.onScrollStateChangedListener = onScrollStateChangedListener; } public int getFixPx(int dp){ float scale=getContext().getResources().getDisplayMetrics().density; return (int)(scale*dp+0.5); } private OnItemClickListener listener; @Override public void setOnItemClickListener(OnItemClickListener listener) { this.listener = listener; // super.setOnItemClickListener(listener); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if(listener==null)return; if(curHeadView != null){ if(position==0)return; listener.onItemClick(parent, view, position-1, id); }else{ listener.onItemClick(parent, view, position, id); } } }
使用很方便,只需调用
LoaderListView.setLoadNotifyer(LoadNotifyer loadNotifyer)
然后每次滑到底部需要加载更多数据的时候,就会回调
LoadNotifyer.load()
然后你在 load() 方法里加载下一页数据,加载完毕调用
Adapter.notifyDataSetChanged()
列表就展示新数据了!
此外我这里面还封装了一个神奇的功能是设置 FootView 状态:
这里有四种状态:
public enum FOOTVIEW_TYPE { /** 加载中 */ LOADING, /** 没有更多了,返回顶部 */ NOMOR, /** 加载失败重试 */ RETRY, /**无*/ NONE }在加载下一页失败的时候,调用
listView.setFootviewType(FOOTVIEW_TYPE.RETRY)
列表底部显示改为,加载失败,点击重试,用户点击之后,会再次回调你的 load() 方法
同理
当你没有更多数据的时候调用
listView.setFootviewType(FOOTVIEW_TYPE.NOMOR)
列表底部显示改为 回到顶部 用户点击后自动跳到第一行!