Android 動態加載 ListView 實現

jopen 11年前發布 | 29K 次閱讀 Android Android開發 移動開發

摘要: 如今動態加載成為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)
列表底部顯示改為 回到頂部 用戶點擊后自動跳到第一行!

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!