原文出處:http://givemepass.blogspot.tw/2012/08/progressdialog.html
在如何使用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<URL, Integer, String> { 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類別。