zoukankan      html  css  js  c++  java
  • Android 上拉加载更多功能

    前几天看了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 }

    好了,最简单的上拉加载就是这个样子了。我已经把它集成进了自己的项目里面。

  • 相关阅读:
    HDU 4861 Couple doubi(数论)
    POJ 3233 Matrix Power Series 二分+矩阵乘法
    js原生offsetParent解析
    ADS-B显示终端6.8
    模板
    习题四——数字智力题
    Android ORMLite ForeignCollection关联外部集合
    Android应用程序无法读写USB设备的解决方法
    [学习笔记]批次需求计划-十一大量
    jqury+css实现可弹出伸缩层
  • 原文地址:https://www.cnblogs.com/lout/p/4878715.html
Copyright © 2011-2022 走看看