2015年3月28日 星期六

如何使用ProgressDialog之二



原文出處: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類別。