ProgressBar while loading ListView of Android

ListView通常是用來顯示列表的Widget,但是一般不可能直接從不管是遠端的Database或是Sqlite撈出全部的資料然後直接顯示,一定是分批撈...

ListView通常是用來顯示列表的Widget,但是一般不可能直接從不管是遠端的Database或是Sqlite撈出全部的資料然後直接顯示,一定是分批撈取分批顯示。

這時就需要一個Loading畫面讓Client知道說,當前正在載入資料。不管是跳Dialog或是像Facebook一樣在畫面Bottom顯示Loading layout都可以。

我採取的是像Facebook一樣的方式呈現。

建立MainActivity.java

simpleAdapter可另外設置R.layout.listview_item讓ListView中的資料吃別隻xml。

package com.example.test;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends Activity {

    private String TAG_LOG = MainActivity.class.getName();
    // ViewList需要用到的制定鍵值
    String[] adapterKey;
    // ViewList需要用到的制定鍵值
    int[] adapterViewKey;
    // 顯示讀取畫面的容器
    LinearLayout loadingLayout;
    // 顯示列表的容器
    ListView listView;
    // 列表
    SimpleAdapter simpleAdapter;
    // 列表資料
    List<map object>> items = new ArrayList<map object>>();

    /**
     * 取資料
     * @param items
     * @return
     */
    private List<map object>> getData(List<map object>> items, String[] adapterKey)
    {
        Integer limitCount = items.size() + 10;
        for (Integer count = items.size(); count<limitcount count map object> item = new HashMap<string object>();
            item.put(adapterKey[0], count+1);
            items.add(item);
        }
        return items;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = (ListView) findViewById(R.id.listView);

        // 設定ListView相對應的View
        adapterKey = new String[]{"number"};
        adapterViewKey = new int[]{R.id.txtNumber};

        // 取得資料並且設定Adapter
        items = getData(items, adapterKey);
        simpleAdapter = new SimpleAdapter(
                MainActivity.this,
                items,
                R.layout.listview_item,
                adapterKey,
                adapterViewKey
        );


        // Set loading view before of set setAdapter
        loadingLayout = getLoadingLayout(MainActivity.this, null);
        listView.addFooterView(loadingLayout);
        listView.setAdapter(simpleAdapter);
        listView.setOnScrollListener(moreItemScrollListener);
    }

    // 已經到資料結尾
    boolean isEnd = false;

    /**
     * 聆聽往上滑動讀取更多物件的事件,會在下方顯示讀取狀態
     */
    private AbsListView.OnScrollListener moreItemScrollListener = new AbsListView.OnScrollListener() {

        // 可以再次載資料
        boolean canLoadData = false;
        // 在讀取資料中
        boolean isLoadingMode = false;

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            //如果不是在讀取資料的狀態、還有資料可以讀取
            if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0 && !isLoadingMode && !isEnd) {
                canLoadData = true;
                Log.d(TAG_LOG, "can load");
            }
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

            //當ListView拉到底,且沒在載入資料時,而且還有資料可以讀取
            if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && canLoadData && !isLoadingMode && !isEnd) {
                // 再次撈取資料前先關閉可以撈取資料的狀態
                Log.d(TAG_LOG, "loading");
                isLoadingMode = true;
                loadMoreData();

                //通知Adapter更新
                simpleAdapter.notifyDataSetChanged();
                canLoadData = false;
                isLoadingMode = false;
            }
        }
    };

    /**
     * 處理要求更多物件
     */
    private void loadMoreData() {
        if (this.items.size() = 30 && !isEnd) {
            isEnd = true;
            this.listView.removeFooterView(this.loadingLayout);
            Log.d(TAG_LOG, "can't load again");
        }
    }

    /**
     * Get loading view layout object
     *
     * @param context
     * @param msg
     * @return LinearLayout
     */
    public LinearLayout getLoadingLayout(Context context, String msg) {
        if (msg == null || msg == "")
            msg = context.getResources().getString(R.string.loading_text);

        Log.d("Util", "Loading screen");
        LinearLayout.LayoutParams WClayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        LinearLayout.LayoutParams FFlayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT);

        LinearLayout layout = new LinearLayout(context);
        layout.setOrientation(LinearLayout.HORIZONTAL);
        ProgressBar progressBar = new ProgressBar(context);
        progressBar.setPadding(0, 0, 15, 0);
        layout.addView(progressBar, WClayoutParams);

        TextView textView = new TextView(context);
        textView.setText(msg);
        textView.setGravity(Gravity.CENTER_VERTICAL);

        layout.addView(textView, FFlayoutParams);
        layout.setGravity(Gravity.CENTER);

        LinearLayout loadingLayout = new LinearLayout(context);
        loadingLayout.addView(layout, WClayoutParams);
        loadingLayout.setGravity(Gravity.CENTER);

        return loadingLayout;
    }

}

getLoadingLayout函式是用在當手勢往上滑畫面在最底部時要顯示讀取資料中的Loading畫面。

Loading layout並不是當讀取時才出現,而是他一直置於ListView最底部,所以讀取不到資料時要將Loading layout移除

this.listView.removeFooterView(this.loadingLayout);

執行後就會是這樣

Loading

Loading end

Alan

Read more posts by this author.