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)
列表底部显示改为 回到顶部 用户点击后自动跳到第一行!