摘要:
如今动态加载成为app 必有的功能,本质上我并不需要一次性加载所有东西,尤其是联网加载时候,前几天看到一篇博文讲国外一个开源解决方案,感觉他的做法并不完美于是发表自己的一些拙见!
首先讲原理:
ListView 可以设置一个滚动监听器
1 | android.widget.AbsListView.setOnScrollListener(OnScrollListener l) |
有个方法
1 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) |
这里面有三个参数:
firstVisibleItem:
第一个可见Item在所有Item中的位置(即屏幕上显示的第一行,在你的数据数组中的位置)
visibleItemCount:
可见Item个数(屏幕内可以显示多少行)
totalItemCount:
总共有多少行数据
通过这个3个参数容易想到,如果
firstVisibleItem +
visibleItemCount >=
totalItemCount 不就表明 列表已经滑到了底部么?这个时候就是我们加载数据的时机了!
然后我们需要在列表底部增加一个 item 显示:点击加载更多 或者 正在加载中请稍后 或者 没有更多数据了
这个我们要用到
1 | ListView.addFooterView(View v) |
添加了 footView ,footView 就成了列表最后一行,也就说相对于你的总数据增加了一行,所有这里有一点要注意的地方:
调用这个方法必须在 ListView.SetAdapter() 之前,否则将会影响 Cursor 类适配器
知道了原理就很简单了,下面奉上我封装的 LoaderListView
005 | * @创建日期 2013-3-19 16:01:10 |
009 | public class LoaderListView extends ListView implements |
013 | public interface LoadNotifyer { |
017 | public interface OnScrollStateChangedListener { |
018 | public void onScrollStateChanged( int oldState, int newState); |
021 | private LinearLayout footViewLoading, footViewRetry, footViewNomore; |
022 | private LoadNotifyer loadNotifyer; |
023 | private int scrollState; |
024 | private OnScrollStateChangedListener onScrollStateChangedListener; |
027 | public LoaderListView(Context context, AttributeSet attrs, int defStyle) { |
028 | super (context, attrs, defStyle); |
032 | public LoaderListView(Context context, AttributeSet attrs) { |
033 | super (context, attrs); |
037 | public LoaderListView(Context context) { |
043 | public void onClick(View v) { |
044 | if (v.getId() == 0x1001 ){ |
045 | setFootviewType(FOOTVIEW_TYPE.LOADING); |
046 | if (loadNotifyer != null ) |
048 | } else if (v.getId() == 0x1002 ) { |
053 | private void init(Context context) { |
054 | footViewLoading = new LinearLayout(context); |
055 | footViewLoading.setOrientation(LinearLayout.HORIZONTAL); |
056 | footViewLoading.setGravity(Gravity.CENTER); |
057 | ProgressBar bar = new ProgressBar(context); |
058 | TextView textView = new TextView(context); |
059 | textView.setText( "加载中..." ); |
060 | footViewLoading.addView(bar, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); |
061 | footViewLoading.addView(textView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); |
063 | footViewRetry = new LinearLayout(context); |
064 | footViewRetry.setOrientation(LinearLayout.HORIZONTAL); |
065 | footViewRetry.setGravity(Gravity.CENTER); |
066 | textView = new TextView(context); |
067 | textView.setId( 0x1001 ); |
068 | textView.setGravity(Gravity.CENTER); |
069 | textView.setText( "网络不给力,请重试 o(︶︿︶)o" ); |
070 | textView.setOnClickListener( this ); |
071 | footViewRetry.addView(textView, new LayoutParams(LayoutParams.WRAP_CONTENT, getFixPx( 50 ))); |
073 | footViewNomore = new LinearLayout(context); |
074 | footViewNomore.setOrientation(LinearLayout.HORIZONTAL); |
075 | footViewNomore.setGravity(Gravity.CENTER); |
076 | footViewNomore.setId( 0x1002 ); |
078 | textView = new TextView(context); |
079 | textView.setText( "返回顶部↑" ); |
080 | textView.setGravity(Gravity.CENTER); |
082 | footViewNomore.setClickable( true ); |
083 | footViewNomore.setOnClickListener( this ); |
084 | footViewNomore.addView(textView, new LayoutParams(LayoutParams.WRAP_CONTENT, getFixPx( 50 ))); |
086 | setFootviewType(FOOTVIEW_TYPE.LOADING); |
088 | setOnScrollListener( this ); |
089 | scrollState = SCROLL_STATE_IDLE; |
091 | super .setOnItemClickListener( this ); |
094 | public enum FOOTVIEW_TYPE { |
105 | private View curFootView; |
106 | public void setFootviewType(FOOTVIEW_TYPE type) { |
107 | if (curFootView != null && curFootView.getTag() == type) |
110 | if (curFootView != null ) |
111 | removeFooterView(curFootView); |
115 | curFootView = footViewLoading; |
118 | curFootView = footViewNomore; |
121 | curFootView = footViewRetry; |
127 | addFooterView(curFootView); |
128 | curFootView.setTag(type); |
131 | private View curHeadView; |
132 | public void setHeadView(View v) { |
133 | if (curHeadView!= null ) |
140 | public void onScrollStateChanged(AbsListView view, int scrollState) { |
141 | if (scrollState != this .scrollState) { |
142 | if (onScrollStateChangedListener != null ){ |
143 | onScrollStateChangedListener.onScrollStateChanged( this .scrollState, scrollState); |
145 | this .scrollState = scrollState; |
149 | protected int firstVisibleItem, visibleItemCount, totalItemCount; |
152 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { |
153 | if (totalItemCount < 2 ) |
158 | if (firstVisibleItem + visibleItemCount >= totalItemCount){ |
159 | if (loadNotifyer != null && (curFootView != footViewNomore)) { |
163 | this .firstVisibleItem = firstVisibleItem; |
164 | this .visibleItemCount = visibleItemCount; |
165 | this .totalItemCount = totalItemCount; |
168 | public int getFirstVisibleItem() { |
169 | return firstVisibleItem; |
172 | public int getVisibleItemCount() { |
173 | return visibleItemCount; |
176 | public int getScrollState() { |
180 | public void setLoadNotifyer(LoadNotifyer loadNotifyer) { |
181 | this .loadNotifyer = loadNotifyer; |
184 | public void setOnScrollStateChangedListener(OnScrollStateChangedListener onScrollStateChangedListener) { |
185 | this .onScrollStateChangedListener = onScrollStateChangedListener; |
188 | public int getFixPx( int dp){ |
189 | float scale=getContext().getResources().getDisplayMetrics().density; |
190 | return ( int )(scale*dp+ 0.5 ); |
193 | private OnItemClickListener listener; |
195 | public void setOnItemClickListener(OnItemClickListener listener) { |
196 | this .listener = listener; |
201 | public void onItemClick(AdapterView<?> parent, View view, int position, |
203 | if (listener== null ) return ; |
204 | if (curHeadView != null ){ |
205 | if (position== 0 ) return ; |
206 | listener.onItemClick(parent, view, position- 1 , id); |
208 | listener.onItemClick(parent, view, position, id); |
使用很方便,只需调用
LoaderListView.setLoadNotifyer(LoadNotifyer loadNotifyer)
然后每次滑到底部需要加载更多数据的时候,就会回调
LoadNotifyer.load()
然后你在 load() 方法里加载下一页数据,加载完毕调用
Adapter.notifyDataSetChanged()
列表就展示新数据了!
此外我这里面还封装了一个神奇的功能是设置 FootView 状态:
这里有四种状态:
01 | public enum FOOTVIEW_TYPE { |
在加载下一页失败的时候,调用
listView.setFootviewType(FOOTVIEW_TYPE.RETRY)
列表底部显示改为,加载失败,点击重试,用户点击之后,会再次回调你的 load() 方法
同理
当你没有更多数据的时候调用
listView.setFootviewType(FOOTVIEW_TYPE.NOMOR)
列表底部显示改为 回到顶部 用户点击后自动跳到第一行!
嗯,很好!很强大!必须就顶一个!欢迎拍砖!