如何使用ProgressDialog之二

如何使用ProgressDialog當中, 我們假設一個程式在執行,
會跳出一個ProgressDialog來顯示程式正在執行,
讓使用者不會以為程式當機。

但是實際上, 我們並不知道程式何時才會執行完成,
假設我們在下載一個檔案, 結果網路斷掉, 那麼ProgressDialog還是一直顯示著,
使用者還是會不耐煩的,

因此, 我們需要一個方式, 來計算還剩下多少時間,
這時候, AsyncTask是一個很方便的工具,
他可以幫你去除一些煩人的執行緒問題。



在官方網站有給一個很詳細的範例, 並且使用真正的網路下載來判斷進度,
但是為了理解這個流程, 因此我用假的進度來模擬整個步驟。

首先建立一個專案, 剛開始只是一個很簡單的Activity,
public class ProgressbarDemoActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_progressbar_demo);
    }
}

接下來就是建立我們的AsyncTask,
從官網的範例可以知道,
http://developer.android.com/reference/android/os/AsyncTask.html

在這之前, 我們可以看到網站上說明,
必須先繼承AsyncTask,
private class MyTask extends AsyncTask<Void, Void, Void> { ... }

然後我們必須實作四個方法, 分別是
onPreExecute()
doInBackground(Params...)
onProgressUpdate(Progress...)
onPostExecute(Result)


而這四個方法傳入的參數, 必須和繼承AsyncTask後面的泛型型態是相同的,
先來解釋這四個方法的意義,
第一個方法onPreExecute()可以從名稱知道, 他是在進行一些初始化的工作。

第二個方法是doInBackground()會傳入一個參數, 這個參數的數量是不定的,
也就是說, 你可以傳入零到數個參數也不是問題, 從官網的例子來看,
當宣告這個類別的物件時候, 只要呼叫execute這個方法,
傳入你想下載的網址, 就可以開始使用它了。

new DownloadFilesTask().execute(url1, url2, url3);
官網的範例是傳入三個下載的位置。

第三個方法是onProgressUpdate(), 它一樣會傳入一些參數, 也是不定數量的,
而這個方法通常是在執行onInBackground的時候, 才會去呼叫它,
只要執行publishProgress()這個方法,
就會呼叫onProgressUpdate(), 利用這個方法, 就可以讓程式顯示目前進度為何。

最後一個方法是onPostExecute(), 這個用來接收結果,
通常你在onProgressUpdate()回傳的結果, 就會當參數傳到這個方法,
不過你要注意型態, 才不會沒有傳入。

現在來看一下範例怎麼寫的。
public class TestAsyncTask extends AsyncTask<URLIntegerString> {

    private Context mContext;
    private ProgressDialog mDialog;

    public TestAsyncTask(Context mContext) {
     this.mContext = mContext;
    }

    protected void onPreExecute() {
     
        mDialog = new ProgressDialog(mContext);
        mDialog.setMessage("Loading...");
        mDialog.setCancelable(false);
        mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mDialog.show();
    }
   
    protected String doInBackground(URL... urls) {
        // TODO Auto-generated method stub
     int progress =0;
     while(progress<=100){
         try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         publishProgress(Integer.valueOf(progress));
         progress++;
     }
     
        return "ok";
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        // TODO Auto-generated method stub
        mDialog.setProgress(progress[0]);
    }

    protected void onPostExecute(String result) {
     if(result.equals("ok")){
      mDialog.dismiss();  
     }
    }

}
我用顏色將傳入的參數, 與泛型的參數對應起來,
這樣就可以很清楚知道哪些參數是什麼功用,
雖然從型態就可以了解是如何對應的,
但是萬一我們的型態設定為相同的話, 有可能會造成錯亂,
所以還是了解一下位置比較妥當。

protected void onPreExecute() {
    mDialog = new ProgressDialog(mContext);
    mDialog.setMessage("Loading...");
    mDialog.setCancelable(false);
    mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    mDialog.show();
}

一開始先加入一個ProgressbarDialog用來計算我們的進度,
紅色那行一定要加入, 這樣才可以看到進度。

protected String doInBackground(URL... urls) {
    // TODO Auto-generated method stub
    int progress =0;
    while(progress<=100){
     try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
     publishProgress(Integer.valueOf(progress));
     progress++;
    }
     
    return "ok";
}
為了模擬檔案傳輸速度, 所以故意設定0.05秒就睡一下,
這樣才不會一下子就跑完了,
這邊可以參考官網真正的下載檔案, 然後設定progressbar的進度。

@Override
protected void onProgressUpdate(Integer... progress) {
    // TODO Auto-generated method stub
    mDialog.setProgress(progress[0]);
}
呼叫publishProgress方法就會自動去呼叫onProgressUpdate方法,
然後利用ProgressbarDialog的方法去設定進度。

protected void onPostExecute(String result) {
    if(result.equals("ok")){
     mDialog.dismiss();  
    }
}
當onInBackground結束, 並且return結果的時候, 就會呼叫onPostExecute方法,
這時候我們將dialog消滅, 就可以回到原本的畫面了。


public class ProgressbarDemoActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_progressbar_demo);                                          new TestAsyncTask(this).execute();
    }
}
記得加上這一行, 才能呼叫AsyncTask類別。


程式碼
http://uploadingit.com/file/r6oanakevrc0onmm/ProgressBarDemo.zip