問題:在一開始,我使用了java.util這個函式庫來更新時鐘,但是這個類別在Android上不是個好選擇。在這邊使用時鐘的例子來介紹,在應用程式開一個新線程。考慮到移動裝置應用程式有別於視窗應用程式,我們必須要找到更有效率的方法,來更新時鐘。
應用程式:關於應用程式移植這部份的故事,留待日後的部落格會再開文詳述。如果你對開發應用程式有相當的疑問和興趣,你可以在開發者網站閱讀之前的文章,裡面有關於Matisse(Java上用於開發GUI-圖形使用者介面的類別)的介紹。原本的應用程式是使用Java Swing的SE應用程式。在錄製Podcast的時候,扮演了迴圈計時碼表的角色。當你開始錄音時,碼表同時被啟動,當你按下FLUB按鈕,會紀錄下那時候的音軌。最後你可以儲存它,並且使用Audacity來編輯它。
參考文件:http://www.developer.com/java/ent/print.php/3589961
一開始的版本中,計時器的程式碼如下:
class UpdateTimeTask extends TimerTask { public void run() { long millis = System.currentTimeMillis() - startTime; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; timeLabel.setText(String.format("%d:%02d", minutes, seconds)); } }並在場景上的監聽器來啟動Timer()的更新,詳見以下實例:
if(startTime == 0L) { startTime = evt.getWhen(); timer = new Timer(); timer.schedule(new UpdateTimeTask(), 100, 200); }要特別注意的是,第一個參數100,表示第一次時鐘執行的時候,是在100毫秒之後,第二個參數200,表示之後每隔200毫秒更新一次,直到程式終止。200毫秒感覺並不明顯,但是如果不這樣做的話,就會發生延遲了2秒,畫面卻還沒即時更新的奇怪現象。
當我在使用Android SDK要移植應用程式的時候,發生了程式碼無法在Eclipse上編譯的窘況,原來Timer()這個類別無法被執行(幸好在錯誤訊息中找到原因),除此之外,String.format這個方法也無法使用,所以我必須盡快找到解決的辦法。
最後,計時器的功能,可以透過android.os.Handler這個類別,來設置監聽器。
private Handler mHandler = new Handler(); ... OnClickListener mStartListener = new OnClickListener() { public void onClick(View v) { if (mStartTime == 0L) { mStartTime = System.currentTimeMillis(); mHandler.removeCallbacks(mUpdateTimeTask); mHandler.postDelayed(mUpdateTimeTask, 100); } } };首先,我們無法調用getWhen()這個方法,來對計時器來做設定。於是我們使用了System.currentTimeMills()這個方法,同時設定為Handler.postDelayed()的參數,但是無法重覆取得當下的時間係數。在這種情況下,我們宣告Handler在100毫秒之後,呼叫mUpdateTimeTask()這個方法,雖然解決了宣告計時器的問題,但是卻沒有解決重覆取得時間係數的問題。
我們希望程式能夠重複取得時間係數,直到我們告訴它停下來。為了做到這一點,必須把另一個宣告延遲的函式,放置到mUpdateTimeTask run()這個方法後面。要注意的是,Runnable是Handler實作的方法,所以我們改了一下mUpdateTimeTask去實作,新的時鐘更新方法如下:
private Runnable mUpdateTimeTask = new Runnable() { public void run() { final long start = mStartTime; long millis = SystemClock.uptimeMillis() - start; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; if (seconds < 10) { mTimeLabel.setText("" + minutes + ":0" + seconds); } else { mTimeLabel.setText("" + minutes + ":" + seconds); } mHandler.postAtTime(this, start + (((minutes * 60) + seconds + 1) * 1000)); } };並且可以被定義為類別的成員變數。
if判斷式,是為了讓時間係數的分,在小於10的時候,會以10:06呈現,而不是10:6(希望外來String.format()這個方法能夠使用),在計時器取得時間係數的時候,會調用Handler本身,以每200毫秒來啟動下次的動作: (((minutes * 60) + seconds + 1) * 1000)。
現在我們需要一個按鈕,來讓計時器能夠停止。並且宣告按鈕的監聽器如下:
OnClickListener mStopListener = new OnClickListener() { public void onClick(View v) { mHandler.removeCallbacks(mUpdateTimeTask); } };為了確保按鈕不會發生二次點擊,當按鈕按下的時候,將會移除監聽。
Handler是一個比Timer更好用的類別。Handler能夠妥善處理程式碼在主線程執行,避免額外再新開一個線程。要記得維持線程的順暢,避免搞壞使用者體驗。
以上就是一系列Android之旅的第一個開發提示。希望這能夠在你開發的路上,少走些冤枉路,節省一些時間,可以去開發你想要的程式(即使只是一個應用程式的小更新)。這一篇文章涵蓋了我許多移植應用程式的經驗,也許未來會增加更多的小提示,除此之外,還有更多的討論正在Android Developers Discussion Group進行著。
發文者
原文出處 A Stitch in Time
沒有留言:
張貼留言