當你希望使用Thread來改變UI View內容的時候就必須要搭配Handler來進行存取。
layout/ ui.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="@+id/ProgressDialogSample_ImageView_pImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/progress_circular_background"
/>
<ProgressBar
android:id="@+id/ProgressDialogSample_ProgressBar_pBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="invisible"
/>
<TextView
android:id="@+id/ProgressDialogSample_TextView_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/ProgressDialogSample_ProgressBar_pBar"
android:text="未執行任何步驟。"
/>
</RelativeLayout>
package iamshiao.sample;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class ProgressDialogSample extends Activity {
private final int step1 = 1, step2 = 2, step3 = 3, finish = 4;
private Thread thread = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ui);
//建構執行緒
thread = new Thread(){
@Override
public void run(){
try{
doStep1();
doStep2();
doStep3();
Thread.sleep(3000);
Message msg = new Message();
msg.what = finish;
uiMessageHandler.sendMessage(msg);
}catch (Exception e){
e.printStackTrace();
}finally{
}
}
};
//開始執行執行緒
thread.start();
}
private void doStep1() throws InterruptedException{
Thread.sleep(3000);
Message msg = new Message();
msg.what = step1;
uiMessageHandler.sendMessage(msg);
}
private void doStep2() throws InterruptedException{
Thread.sleep(3000);
Message msg = new Message();
msg.what = step2;
uiMessageHandler.sendMessage(msg);
}
private void doStep3() throws InterruptedException{
Thread.sleep(3000);
Message msg = new Message();
msg.what = step3;
uiMessageHandler.sendMessage(msg);
}
//宣告Handler並同時建構隱含類別實體
Handler uiMessageHandler = new Handler(){
@Override
public void handleMessage(Message msg){
//讀出ui.xml中的進度光棒
final ProgressBar pBar =
(ProgressBar)findViewById(
R.id.ProgressDialogSample_ProgressBar_pBar);
pBar.setVisibility(View.VISIBLE); //開始後設為可見
//讀出ui.xml中用以表示未開始的圖案
final ImageView pImg =
(ImageView)findViewById(R.id.ProgressDialogSample_ImageView_pImg);
pImg.setVisibility(View.INVISIBLE); //開始後設為不可見
//讀出ui.xml中的描述用TextView
TextView tv =
(TextView)findViewById(R.id.ProgressDialogSample_TextView_desc);
switch (msg.what){
case step1:
tv.setText(R.string.processing_step1);
break;
case step2:
tv.setText(R.string.processing_step2);
break;
case step3:
tv.setText(R.string.processing_step3);
break;
case finish:
tv.setText(R.string.finish);
pBar.setVisibility(View.INVISIBLE);
pImg.setVisibility(View.VISIBLE);
tv.setText("完成。");
thread.interrupt();
break;
}
super.handleMessage(msg);
}
};
}
執行緒的部分與前篇大致相同,但由於其中不能直接執行
tv.setText(R.string.processing_step1);
這類UI View存取的動作,所以加入了Handler來控制UI View,Handler的運作是利用handleMessage這個事件來監聽執行緒是否有送出Message,如果有的話利用Message的what屬性等機制來判別要進行什麼動作。 例如程式碼中我宣告了4個常數
private final int step1 = 1, step2 = 2, step3 = 3, finish = 4;
private void doStep1() throws InterruptedException{
Thread.sleep(3000);
Message msg = new Message();
msg.what = step1;
uiMessageHandler.sendMessage(msg);
}
表示執行緒進行的步驟進度,而執行緒執行副程式doStep1~3的這個過程中,副程式在sleep完成後就會利用Handler變數的sendMessage來送出對應其步驟的Message變數。
//宣告Handler並同時建構隱含類別實體
Handler uiMessageHandler = new Handler(){
@Override
public void handleMessage(Message msg){
...
switch (msg.what){
case step1:
tv.setText(R.string.processing_step1);
break;
...
}
super.handleMessage(msg);
}
};
最後handleMessage事件再接聽到sendMessage之後就利用Message變數的what屬性之類的機制來判讀、執行對應動作。
重點提醒,使用Thread&Handler要特別注意...
- handleMessage必須有@Override標註表示覆寫。
- 每個Message變數必須重新new,只改動what內容仍會被視為同樣的Message,而造成非預期的錯誤。
Thread.sleep(3000);
Message msg = new Message();
msg.what = step1;
uiMessageHandler.sendMessage(msg);
Thread.sleep(3000);
msg.what = step2; //X 同Message實體只變更what就重複使用會造成非預期錯誤。
uiMessageHandler.sendMessage(msg);
Thread.sleep(3000);
msg = new Message(); //O Message有重新建構實體才正確。
msg.what = step3;
uiMessageHandler.sendMessage(msg);
- 任何UI View的控制應該在handleMessage中進行,而不是直接在Thread中進行。
補充
Message變數也可以透過setData方法來攜帶Bundle實體傳遞資料。
Message msg = new Message();
Bundle data = new Bundle();
data.putString("data", "data");
msg.setData(data);
msg.what = step2;
uiMessageHandler.sendMessage(msg);
case step2:
tv.setText(msg.getData().getString("data"));
转载地址:http://iamshiao.blogspot.com/2010/12/androidhandlerthread.html