範例說明
Android的Widget,有許多是為了與User互動而特別設計的,但也有部分是作為程式提示、顯示程式運行狀態的Widget。現在介紹的範例,與前一章介紹過的ProgressDialog對話方塊的應用目的相似,但由於前章介紹的ProgressDialog是繼承自Android.app.ProgressDialog所設計的互動交談視窗,在應用時,必須建立ProgressDialog對象,在運行時會彈出“對話方塊”作為提醒,此時應用程式後台失去焦點,直到進程結束後,才會將控制權交給應用程式,如果在Activity當中不希望後台失焦,又希望提示User有某背景程式正處於忙碌階段,此時,ProgressBar就會派上用場了。
Android提供的ProgressBar Widget控制項與ProgressDialog應用目標不同,在程式一開始即可在main.xml Layout當中布局,先將部署在Layout裡的ProgressBar的屬性設為隱藏(一開始看不見),而後使用進程來“假裝”程式忙碌中,但不同的是,可在進程當中取得運行時的進度,在“運行”的過程中,將運行進度通過TextView顯示出來。本範例除了學習ProgressBar Widget的顯示及使用之外,另一個學習關鍵則是Handler的使用,因為新起的進程無法訪問Activity裡的Widget,也無法將運行狀態外送出來,所以需要通過Handler及Message對象,將進程裡的狀態往外傳遞,最後由Activity的Handler事件接收取得啟動並執行狀態。
範常式序
src/irdc.ex04_17/EX04_17.java
為了讓Thread運行過程中,可以不斷地將資訊往Activity傳遞,所以用了Android.os.Handler對象及Android.os.Message對象,且在類成員變數中聲明了兩個整數:GUI_STOP_NOTIFIER與GUI_THREADING_NOTIFIER,這兩個整數將作為資訊傳遞出來時的訊號標識,前者為當Thread需要喊停的時候處理,後者為進程正在運行過程中所需處理的標識。
程式中設計了一個按鈕,此按鈕的工作是讓原本部署在main.xml裡的ProgressBar顯示出來(原來是設定為Android:visibility="gone"),而因為預設在main.xml中沒有指定它的indeterm- inate屬性,所以即便在程式中強制調用了ProgressBar的setIndeterminate()方法,也無法改變ProgressBar.getProgress()的值,這個值將永遠為0。因此,筆者想要使用循環圖表片動畫作為運行過程中的動畫,並用了一個Counter(整數)來遞增,表示啟動並執行百分比。
複製代碼 代碼如下:/* import程式略 */
public class EX04_17 extends Activity
{
private TextView mTextView01;
private Button mButton01;
private ProgressBar mProgressBar01;
public int intCounter=0;
/* 自訂Handler資訊代碼,用以作為標識事件處理 */
protected static final int GUI_STOP_NOTIFIER = 0x108;
protected static final int GUI_THREADING_NOTIFIER = 0x109;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton01 = (Button)findViewById(R.id.myButton1);
mTextView01 = (TextView)findViewById(R.id.myTextView1);
/* 設定ProgressBar widget對象 */
mProgressBar01 = (ProgressBar)findViewById(R.id.myProgressBar1);
/* 調用setIndeterminate方法賦值indeterminate模式為false */
mProgressBar01.setIndeterminate(false);
/* 當單擊按鈕後,開始進程工作 */
mButton01.setOnClickListener(new Button.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
/* 單擊按鈕讓ProgressBar顯示 */
mTextView01.setText(R.string.str_progress_start);
/* 將隱藏的ProgressBar顯示出來 */
mProgressBar01.setVisibility(View.VISIBLE);
/* 指定Progress為最多100 */
mProgressBar01.setMax(100);
/* 初始Progress為0 */
mProgressBar01.setProgress(0);
/* 開始一個進程 */
new Thread(new Runnable()
{
public void run()
{
/* 預設0至9,共運行10次的迴圈語句 */
for (int i=0;i<10;i++)
{
try
{
/* 成員變數,用以識別載入進度 */
intCounter = (i+1)*20;
/* 每運行一次迴圈,即暫停1秒 */
Thread.sleep(1000);
/* 當Thread運行5秒後顯示運行結束 */
if(i==4)
{
/* 以Message對象,傳遞參數給Handler */
Message m = new Message();
/* 以what屬性指定User自訂 */
m.what = EX04_17.GUI_STOP_NOTIFIER;
EX04_17.this.myMessageHandler.sendMessage(m);
break;
}
else
{
Message m = new Message();
m.what = EX04_17.GUI_THREADING_NOTIFIER;
EX04_17.this.myMessageHandler.sendMessage(m);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}).start();
}
});
}
/* Handler構建之後,會監聽傳來的資訊代碼 */
Handler myMessageHandler = new Handler()
{
// @Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
/* 當取得標識為離開進程時所取得的資訊 */
case EX04_17.GUI_STOP_NOTIFIER:
/* 顯示運行終了 */
mTextView01.setText(R.string.str_progress_done);
/* 設定ProgressBar Widget為隱藏 */
mProgressBar01.setVisibility(View.GONE);
Thread.currentThread().interrupt();
break;
/* 當取得標識為持續在進程當中時所取得的資訊 */
case EX04_17.GUI_THREADING_NOTIFIER:
if(!Thread.currentThread().isInterrupted())
{
mProgressBar01.setProgress(intCounter);
/* 將顯示進度顯示於TextView當中 */
mTextView01.setText
(
getResources().getText(R.string.str_progress_start)+
"("+Integer.toString(intCounter)+"%)/n"+
"Progress:"+
Integer.toString(mProgressBar01.getProgress())+
"/n"+"Indeterminate:"+
Boolean.toString(mProgressBar01.isIndeterminate())
);
}
break;
}
super.handleMessage(msg);
}
};
}
擴充學習
範常式序中,調用mProgressBar01.setIndeterminate(false),不顯示背景進度Bar,若設定為mProgressBar01.setIndeterminate(true),也無法讓預設的ProgressBar圖片(轉圈圈)有正確的進度提示,理由是預設的ProgressBar不支援indeterminate mode循環圖表片方式,所以即便setIn- determinate(true)也無法正確顯示進度。在本程式中,為刻意寫出作為對照練習,一般在未知“進度”的情況下,可改用文字的方式顯示進度百分比,通過調用mProgressBar01.getProgress()取得運行進度值,顯示在文字中。請將Layout裡的ProgressBar Widget定義中,加上一個android: indeterminateOnly屬性,指定其值為false,不顯示後台進度Bar.
如下所示:
複製代碼 代碼如下:<ProgressBar
android:id="@+id/myProgressBar1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:max="100"
android:progress="0"
android:orientation="horizontal"
android:progressBarStyle=
"@android:style/Widget.ProgressBar.Horizontal"
android:indeterminateOnly="false"
android:visibility="gone"
/>
ProgressBar除了上述關於Android:progressBarStyle的屬性設定之外,筆者也調查了線上Android的原始碼(http://source.android.com),一些原本Android所使用的progressBarStyleHori- zontal屬性,除預設“圓形”的圖片之外,還有其他的主題及方形圖片Drawable模式可以使用。複製代碼 代碼如下:<resources>
<declare-styleable name="Theme">
<!-- snip -->
<attr name="progressBarStyleHorizontal" format="reference" />
</resources>
接下來看看,這段主題中的屬性名稱progressBarStyleHorizontal定義在frameworks/base/ core/res/res/values/ styles.xml裡,如下所示:複製代碼 代碼如下:<resources>
<style name="Widget.ProgressBar.Horizontal">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">
@android:drawable/progress_horizontal
</item>
<item name="android:indeterminateDrawable">
@android:drawable/progress_indeterminate_horizontal
</item>
<item name="android:minHeight">20dip</item>
<item name="android:maxHeight">20dip</item>
</style>
</resources>
由此可見,如果想讓Android使用其他樣式的ProgressBar,可以在原本的Layout(main.xml)裡添加以下兩項屬性,以觀察運行過程中的圖片變化。複製代碼 代碼如下:android:progressDrawable="@android:drawable/progress_horizontal"
android:indeterminateDrawable=
"@android:drawable/progress_indeterminate_horizontal"