前几天看了github上面的例子,参照它的实现,自己又稍微改了一点,往项目里面增加了一个上拉加载更多功能。具体的实现如下:
首先要重写ListView:
2
3 import android.content.Context;
4 import android.util.AttributeSet;
5 import android.widget.AbsListView;
6 import android.widget.HeaderViewListAdapter;
7 import android.widget.ListAdapter;
8 import android.widget.ListView;
9
10 import java.util.List;
11
12 import njucm.edu.loadmore.activities.LoadingView;
13 import njucm.edu.loadmore.adapters.PagingBaseAdapter;
14
15 /**
16 * Created by Mesogene on 10/9/15.
17 */
18 public class LoadListView extends ListView {
19
20 private OnScrollListener onScrollListener = null;
21 private PageEnableListener pageEnableListener = null;
22 LoadingView loadListView = null;
23 private boolean isLoading;
24 private boolean hasMoreItem;
25 private int lastVisibleItem; //最后一个可见的项
26 private int totalItemCount; //总的项数
27
28
29 public LoadListView(Context context) {
30 super(context);
31 init();
32 }
33
34 public LoadListView(Context context, AttributeSet attrs) {
35 super(context, attrs);
36 init();
37 }
38
39 public LoadListView(Context context, AttributeSet attrs, int defStyleAttr) {
40 super(context, attrs, defStyleAttr);
41 init();
42 }
43
44 public void setPageEnableListener(PageEnableListener pageEnableListener) {
45 this.pageEnableListener = pageEnableListener;
46 }
47
48 public boolean isLoading() {
49 return isLoading;
50 }
51
52 public void setIsLoading(boolean isLoading) {
53 this.isLoading = isLoading;
54 }
55
56 public boolean isHasMoreItem() {
57 return hasMoreItem;
58 }
59
60 public void setHasMoreItem(boolean hasMoreItem) {
61 this.hasMoreItem = hasMoreItem;
62 if(!this.hasMoreItem){ //如果没有更多项,移除底部
63 removeFooterView(loadListView);
64 }
65 else if(findViewById(R.id.loading_view) == null){
66 addFooterView(loadListView);
67 ListAdapter adapter = ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();
68 setAdapter(adapter);
69 }
70 }
71
72 /**
73 * 在下载任务完成之后去调用这个方法
74 * @param hasMoreItem 是否还有更多项
75 * @param newItems 新的项
76 */
77 public void onFinsihLoading(boolean hasMoreItem, List<? extends Object> newItems){
78 setHasMoreItem(hasMoreItem);
79 setIsLoading(false); //下载任务完成后,把loading设置成false
80 if(newItems != null && newItems.size() >0){
81 ListAdapter adapter = ((HeaderViewListAdapter)getAdapter()).getWrappedAdapter(); //获取这个listview的adapter
82 if(adapter instanceof PagingBaseAdapter){
83 ((PagingBaseAdapter) adapter).addMoreItems(newItems); //添加项目,包含notify方法
84 }
85 }
86 }
87 /**
88 * 初始化listview的操作
89 */
90 private void init(){
91 isLoading = false;
92 loadListView = new LoadingView(getContext());
93 addFooterView(loadListView);
94 super.setOnScrollListener(new OnScrollListener() {
95 @Override
96 public void onScrollStateChanged(AbsListView absListView, int scrollState) {
97 if(onScrollListener != null){
98 onScrollListener.onScrollStateChanged(absListView, scrollState);
99 }
100 /**
101 * 当你的listview移动到底部的时候,即你看到的最后一项等于总的项数,已经停止滚动 没有正在加载并且还有更多项
102 * 的时候会被执行
103 */
104 if(lastVisibleItem == totalItemCount && scrollState == SCROLL_STATE_IDLE && !isLoading && hasMoreItem){
105 if(pageEnableListener != null){
106 isLoading = true; //执行之后的状态就是loading
107 pageEnableListener.onLoadMoreItems(); //调用回调方法
108 }
109 }
110 }
111
112 @Override
113 public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totleItem) {
114 //Dispatch to child OnScrollListener
115 if (onScrollListener != null) {
116 onScrollListener.onScroll(absListView, firstVisibleItem, visibleItemCount, totleItem);
117 }
118 lastVisibleItem = firstVisibleItem + visibleItemCount; //最后看到的一项
119 totalItemCount = totleItem; //总的项数
120 }
121 });
122
123 }
124
125 @Override
126 public void setOnScrollListener(OnScrollListener onScrollListener) {
127 this.onScrollListener = onScrollListener;
128 }
129
130 public interface PageEnableListener{
131 public void onLoadMoreItems();
132 }
133
134 }
我们可以看到还要加一个loadingview的,就是你的正在加载界面,这个界面会被动态添加到你的footview里面的:
1 package njucm.edu.loadmore.activities;
2
3 import android.content.Context;
4 import android.util.AttributeSet;
5 import android.widget.LinearLayout;
6
7 import njucm.edu.loadmore.R;
8
9 /**
10 * Created by Mesogene on 10/10/15.
11 */
12 public class LoadingView extends LinearLayout {
13
14 public LoadingView(Context context) {
15 super(context);
16 init();
17 }
18
19 public LoadingView(Context context, AttributeSet attrs) {
20 super(context, attrs);
21 init();
22 }
23
24 public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
25 super(context, attrs, defStyleAttr);
26 init();
27 }
28
29 public void init(){
30 inflate(getContext(), R.layout.loadinflinear, this);
31 }
32
33 }
只是简单重写了一下LinearLayout, 下面只要写一个loading的不局文件就行了
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_margin="@dimen/dp_10"
android:id="@+id/loading_view">
<ProgressBar
android:id="@+id/video_item_image"
style="?android:progressBarStyle"
android:layout_width="@dimen/loading_view_progress_size"
android:layout_height="@dimen/loading_view_progress_size"
android:layout_marginRight="@dimen/loading_view_margin_right"
android:indeterminate="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loading"
android:layout_gravity="center"
android:textColor="@color/material_blue_grey_800"
android:textSize="@dimen/sp_18"/>
</LinearLayout>
这样基本就已经好了,那么还有一个listview的父类的适配器要自己写一下:
1 import android.widget.BaseAdapter;
2 import android.widget.ListAdapter;
3
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import njucm.edu.loadmore.LoadListView;
8
9 /**
10 * Created by Mesogene on 10/12/15.
11 */
12 public abstract class PagingBaseAdapter<T> extends BaseAdapter {
13
14 protected List<T> items = null;
15
16 public PagingBaseAdapter(List<T> items) {
17 this.items = items;
18 }
19
20 public PagingBaseAdapter(){
21 this.items = new ArrayList<>();
22 }
23
24 public void addMoreItems(List<T> items){
25 this.items.addAll(items); //把新的项添加到listview里面
26 notifyDataSetChanged(); //更新布局
27 }
28
29 }
这样之后,你自己的listviewAdapter就可以继承这个类,你的adapter拥有绘制每一个listitem的功能和添加下一页数据项的功能。
1 package njucm.edu.loadmore.adapters;
2
3 import android.view.LayoutInflater;
4 import android.view.View;
5 import android.view.ViewGroup;
6 import android.widget.TextView;
7
8 import java.util.List;
9
10 /**
11 * Created by Mesogene on 10/12/15.
12 */
13 public class MyListAdapter extends PagingBaseAdapter<String> {
14
15 @Override
16 public int getCount() {
17 return items.size();
18 }
19
20 @Override
21 public String getItem(int position) {
22 return items.get(position);
23 }
24
25 @Override
26 public long getItemId(int position) {
27 return position;
28 }
29
30 @Override
31 public View getView(int position, View view, ViewGroup viewGroup) {
32 TextView textView;
33 String text = getItem(position);
34 if(view != null){
35 textView = (TextView) view;
36 }else {
37 textView = (TextView) LayoutInflater.from(viewGroup.getContext()).inflate(android.R.layout.simple_list_item_1, null);
38 }
39 textView.setText(text);
40 return textView;
41 }
42 }
最后就是如何使用了,再onCreate() 或者 onCreateView() 添加如下代码,我们发现它有一个异步加载的过程,使用到了线程
1 li.setAdapter(adapter);
2 li.setHasMoreItem(true);
3 li.setPageEnableListener(new LoadListView.PageEnableListener() {
4 @Override
5 public void onLoadMoreItems() {
6 if(pager < 3){
7 new CountryAsyncTask().execute();
8 }else{
9 li.onFinsihLoading(false, null);
10 }
11 }
12 });
1 private class CountryAsyncTask extends SafeAsyncTask<List<String>>{
2 @Override
3 public List<String> call() throws Exception { //模拟后台下载数据
4 List result = null;
5 switch (pager){
6 case 0:
7 result = firstList;
8 break;
9 case 1:
10 result = secondList;
11 break;
12 case 2:
13 result = thirdList;
14 break;
15 }
16 Thread.sleep(3000);
17 return result;
18 }
19
20 @Override
21 protected void onSuccess(List<String> strings) throws Exception {
22 super.onSuccess(strings);
23 pager++;
24 li.onFinsihLoading(true, strings); //下载成功之后调用的方法,更新UI
25 }
26 }
这里面可能要自己添加一些数据在firstlist等里面。 还有下面是这个类似于AsyncTask但又不是的,这个类的代码如下
1 package njucm.edu.loadmore.activities;
2
3 import android.os.Handler;
4 import android.os.Looper;
5 import android.util.Log;
6
7 import java.io.InterruptedIOException;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.concurrent.Callable;
11 import java.util.concurrent.CountDownLatch;
12 import java.util.concurrent.Executor;
13 import java.util.concurrent.Executors;
14 import java.util.concurrent.FutureTask;
15
16
17 /**
18 * A class similar but unrelated to android's {@link android.os.AsyncTask}.
19 * <p/>
20 * Unlike AsyncTask, this class properly propagates exceptions.
21 * <p/>
22 * If you're familiar with AsyncTask and are looking for {@link android.os.AsyncTask#doInBackground(Object[])},
23 * we've named it {@link #call()} here to conform with java 1.5's {@link java.util.concurrent.Callable} interface.
24 * <p/>
25 * Current limitations: does not yet handle progress, although it shouldn't be
26 * hard to add.
27 * <p/>
28 * If using your own executor, you must call future() to get a runnable you can execute.
29 *
30 * @param <ResultT>
31 */
32 public abstract class SafeAsyncTask<ResultT> implements Callable<ResultT> { //Callable可以返回任意类型。1.5 以后加入
33 public static final int DEFAULT_POOL_SIZE = 25; //默认的线程池的大小是25
34 protected static final Executor DEFAULT_EXECUTOR = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE); //利用线程池
35
36 protected Handler handler;
37 protected Executor executor;
38 protected StackTraceElement[] launchLocation;
39 protected FutureTask<Void> future;
40
41
42 /**
43 * Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE) and
44 * Handler to new Handler()
45 */
46 public SafeAsyncTask() {
47 this.executor = DEFAULT_EXECUTOR;
48 }
49
50 /**
51 * Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE)
52 */
53 public SafeAsyncTask(Handler handler) {
54 this.handler = handler;
55 this.executor = DEFAULT_EXECUTOR; //线程池用默认的设置
56 }
57
58 /**
59 * Sets Handler to new Handler()
60 */
61 public SafeAsyncTask(Executor executor) {
62 this.executor = executor;
63 }
64
65 public SafeAsyncTask(Handler handler, Executor executor) {
66 this.handler = handler;
67 this.executor = executor;
68 }
69
70
71 public FutureTask<Void> future() {
72 future = new FutureTask<Void>(newTask());
73 return future;
74 }
75
76 public SafeAsyncTask<ResultT> executor(Executor executor) {
77 this.executor = executor;
78 return this;
79 }
80
81 public Executor executor() {
82 return executor;
83 }
84
85 public SafeAsyncTask<ResultT> handler(Handler handler) {
86 this.handler = handler;
87 return this;
88 }
89
90 public Handler handler() {
91 return handler;
92 }
93
94 public void execute() {
95 execute(Thread.currentThread().getStackTrace());
96 }
97
98 protected void execute(StackTraceElement[] launchLocation) {
99 this.launchLocation = launchLocation;
100 executor.execute(future());
101 }
102
103 public boolean cancel(boolean mayInterruptIfRunning) {
104 if (future == null) throw new UnsupportedOperationException("You cannot cancel this task before calling future()");
105
106 return future.cancel(mayInterruptIfRunning);
107 }
108
109
110 /**
111 * @throws Exception, captured on passed to onException() if present.
112 */
113 protected void onPreExecute() throws Exception {
114 }
115
116 /**
117 * @param t the result of {@link #call()}
118 * @throws Exception, captured on passed to onException() if present.
119 */
120 @SuppressWarnings({"UnusedDeclaration"})
121 protected void onSuccess(ResultT t) throws Exception {
122 }
123
124 /**
125 * Called when the thread has been interrupted, likely because
126 * the task was canceled.
127 * <p/>
128 * By default, calls {@link #onException(Exception)}, but this method
129 * may be overridden to handle interruptions differently than other
130 * exceptions.
131 *
132 * @param e an InterruptedException or InterruptedIOException
133 */
134 protected void onInterrupted(Exception e) {
135 onException(e);
136 }
137
138 /**
139 * Logs the exception as an Error by default, but this method may
140 * be overridden by subclasses.
141 *
142 * @param e the exception thrown from {@link #onPreExecute()}, {@link #call()}, or {@link #onSuccess(Object)}
143 * @throws RuntimeException, ignored
144 */
145 protected void onException(Exception e) throws RuntimeException {
146 onThrowable(e);
147 }
148
149 protected void onThrowable(Throwable t) throws RuntimeException {
150 Log.e("roboguice", "Throwable caught during background processing", t);
151 }
152
153 /**
154 * @throws RuntimeException, ignored
155 */
156 protected void onFinally() throws RuntimeException {
157 }
158
159
160 protected Task<ResultT> newTask() {
161 return new Task<ResultT>(this);
162 }
163
164
165 public static class Task<ResultT> implements Callable<Void> {
166 protected SafeAsyncTask<ResultT> parent;
167 protected Handler handler;
168
169 public Task(SafeAsyncTask<ResultT> parent) {
170 this.parent = parent;
171 this.handler = parent.handler != null ? parent.handler : new Handler(Looper.getMainLooper());
172 }
173
174 public Void call() throws Exception {
175 try {
176 doPreExecute();
177 doSuccess(doCall());
178
179 } catch (final Exception e) {
180 try {
181 doException(e);
182 } catch (Exception f) {
183 // logged but ignored
184 Log.e("BACKGROUND_TASK", "Exception in", f);
185 }
186
187 } catch (final Throwable t) {
188 try {
189 doThrowable(t);
190 } catch (Exception f) {
191 // logged but ignored
192 Log.e("BACKGROUND_TASK", "Exception in", f);
193 }
194 } finally {
195 doFinally();
196 }
197
198 return null;
199 }
200
201 protected void doPreExecute() throws Exception {
202 postToUiThreadAndWait(new Callable<Object>() {
203 public Object call() throws Exception {
204 parent.onPreExecute();
205 return null;
206 }
207 });
208 }
209
210 protected ResultT doCall() throws Exception {
211 return parent.call();
212 }
213
214 protected void doSuccess(final ResultT r) throws Exception {
215 postToUiThreadAndWait(new Callable<Object>() {
216 public Object call() throws Exception {
217 parent.onSuccess(r);
218 return null;
219 }
220 });
221 }
222
223 protected void doException(final Exception e) throws Exception {
224 if (parent.launchLocation != null) {
225 final ArrayList<StackTraceElement> stack = new ArrayList<StackTraceElement>(Arrays.asList(e.getStackTrace()));
226 stack.addAll(Arrays.asList(parent.launchLocation));
227 e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()]));
228 }
229 postToUiThreadAndWait(new Callable<Object>() {
230 public Object call() throws Exception {
231 if (e instanceof InterruptedException || e instanceof InterruptedIOException) parent.onInterrupted(e);
232 else parent.onException(e);
233 return null;
234 }
235 });
236 }
237
238 protected void doThrowable(final Throwable e) throws Exception {
239 if (parent.launchLocation != null) {
240 final ArrayList<StackTraceElement> stack = new ArrayList<StackTraceElement>(Arrays.asList(e.getStackTrace()));
241 stack.addAll(Arrays.asList(parent.launchLocation));
242 e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()]));
243 }
244 postToUiThreadAndWait(new Callable<Object>() {
245 public Object call() throws Exception {
246 parent.onThrowable(e);
247 return null;
248 }
249 });
250 }
251
252 protected void doFinally() throws Exception {
253 postToUiThreadAndWait(new Callable<Object>() {
254 public Object call() throws Exception {
255 parent.onFinally();
256 return null;
257 }
258 });
259 }
260
261
262 /**
263 * Posts the specified runnable to the UI thread using a handler,
264 * and waits for operation to finish. If there's an exception,
265 * it captures it and rethrows it.
266 *
267 * @param c the callable to post
268 * @throws Exception on error
269 */
270 protected void postToUiThreadAndWait(final Callable c) throws Exception {
271 final CountDownLatch latch = new CountDownLatch(1);
272 final Exception[] exceptions = new Exception[1];
273
274 // Execute onSuccess in the UI thread, but wait
275 // for it to complete.
276 // If it throws an exception, capture that exception
277 // and rethrow it later.
278 handler.post(new Runnable() {
279 public void run() {
280 try {
281 c.call();
282 } catch (Exception e) {
283 exceptions[0] = e;
284 } finally {
285 latch.countDown();
286 }
287 }
288 });
289
290 // Wait for onSuccess to finish
291 latch.await();
292
293 if (exceptions[0] != null) throw exceptions[0];
294
295 }
296
297 }
298
299 }
好了,最简单的上拉加载就是这个样子了。我已经把它集成进了自己的项目里面。