2012-11-20 38 views
5

我正在設計適用於Android 2.2的計時器/倒數計時器應用程序,並希望單擊一次按鈕即可同時啓動計時器。所以,理想情況下,我想在秒(時間)天文鐘和計時器在同一個實例改變。 (即使計時器正在計數,計時器也會倒計時)。由於我使用的是由Android提供的天文臺和定時器功能,我寫了下面的代碼,當用戶按下「開始」按鈕同時在Android中啓動兩個(或更多)功能

private boolean mStartPressedOnce = false; 
long mTimeWhenStopped = 0; 
Chronometer mChronometer; 
MyCounter mCounter; 
... 
@Override 
public void onClick(View v) { 
    switch (v.getId()) { 
    case R.id.StartButton: 
     // Perform some initialization for the chronometer depending 
     // on the button press 
     if (mStartPressedOnce == false) { 
      mChronometer.setBase(SystemClock.elapsedRealtime()); 
     } else { 
      mChronometer.setBase(SystemClock.elapsedRealtime() + mTimeWhenStopped); 
     } 

     // Perform the initialization for the timer 
     mCounter = new MyCount(45000, 1000); 

     // Fire up the chronometer 
     mChronometer.start(); 

     // Fire up the timer 
     mCounter.start(); 
     break; 

     case R.id.ResetButton: 
      // Reset the chronometer 
      mChronometer.setBase(SystemClock.elapsedRealtime()); 
      mTimeWhenStopped = 0; 
      break; 

     case case R.id.StopButton: 
      mStartPressedOnce = true; 
      // Stop the chronometer 
      mTimeWhenStopped = mChronometer.getBase() - SystemClock.elapsedRealtime(); 
      mChronometer.stop(); 
      break; 
    } 

... 

public class MyCounter extends CountDownTimer { 

    @Override 
    public MyCount(long millisInFuture, long countDownInterval) { 
     super(millisInFuture, countDownInterval); 
    } 

    @Override 
    public void onFinish() {   
     // Nothing to do here 
    } 

    @Override 
    public void onTick(long millisUntilFinished) { 
     long seconds = (long) (millisUntilFinished/1000); 
     long minutes = (long) ((millisUntilFinished/1000)/60); 
     long hours = (long) (((millisUntilFinished/1000)/60)/60); 

     // Do some formatting to make seconds, minutes and hours look pretty  

     // Update the timer TextView    
     (TextView) findViewById(R.id.CountDownTimerTextView)) 
      .setText(hours + ":" + minutes + ":" + seconds); 
    } 
} 

雖然它看起來記時計和計時器秒數等在同步最初爲,經過短時間後,它們似乎熄滅,並且第二次更新都發生在不同的時間。

想知道我能做些什麼來解決這個問題。我沒碰到過 - 讀這個線程

Running multiple AsyncTasks at the same time -- not possible?

我認識到,有可能是設計變更需要的,但我不知道需要做什麼。

編輯:包括類型天文臺和定時器和方法使用天文臺計算的時間 - 每jolivier和njzk2的建議

+2

什麼是你的mChronometer和mCounter,你如何測量時間? – njzk2

+0

爲什麼使用小部件而不是System.nanoTime()或currentTimeMillis()? – Shark

+0

@Shark,不知道我知道你的問題的答案。謝謝。 – aLearner

回答

1

所以,咀嚼這一段時間,都會響起慷慨與我們分享的建議jolivier後,我意識到,存在一個名爲onChronometerTick方法被稱爲每次有天文臺表刻度(2回1次,在這案件)。所以,我想每次調用方法時都會從計數器中減去1000毫秒,並相應地更新計時器顯示。我徹底擺脫了Android計時器片(CountDownTimer)。我想這將是一個很好的方式來同時顯示更新。這也是一個簡單的計時器實現。

我很高興地報告說,它似乎工作得很好。計時器和計時器顯示器確實同時更新。所以,原來的問題看起來像是回答。不幸的是,我在定時器前面碰到了一個錯誤,我用一個醜陋的黑客修復了。我發佈了迄今爲止我所擁有的內容。歡迎任何關於如何解決黑客或改進代碼的建議。請注意,我已經評論了該代碼,以便輕鬆理解所做的事情。

編輯錯誤:我還注意到的另一件事是,大約10分鐘左右後,計時器和計時器關閉一秒鐘。更確切地說,計時器在天文鐘後面一秒鐘。還不確定爲什麼會發生這種情況

private boolean mStartPressedOnce = false; 
long mTimeWhenStopped = 0; 
Chronometer mChronometer; 
long millisUntilFinished = 0; 
boolean firstPassOver = false; 
int counter = 0; 
... 
@Override 
public void onClick(View v) { 
    switch (v.getId()) { 
    case R.id.StartButton: 
     // Perform some initialization for the chronometer depending 
     // on the button press 
     if (mStartPressedOnce == false) { 
      mChronometer.setBase(SystemClock.elapsedRealtime()); 
     } else { 
      mChronometer.setBase(SystemClock.elapsedRealtime() + mTimeWhenStopped); 
     } 

     // Fire up the chronometer 
     mChronometer.start(); 
     break; 

     case R.id.ResetButton: 
      // Reset the chronometer 
      mChronometer.setBase(SystemClock.elapsedRealtime()); 
      mTimeWhenStopped = 0; 
      break; 

     case case R.id.StopButton: 
      mStartPressedOnce = true; 
      // Stop the chronometer 
      mTimeWhenStopped = mChronometer.getBase() - SystemClock.elapsedRealtime(); 
      mChronometer.stop(); 
      break; 
    } 

... 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.stop_watch); 

    mChronometer = (Chronometer) findViewById(R.id.StopWatchTextView); 

    // Initialize the number of milliseconds before the timer expires (
    // set the timer) - in this case to 46 seconds 
    millisUntilFinished = 46000; 

    // Display how many seconds remain before the timer expires 
    ((TextView) findViewById(R.id.CountDownTimerTextView)).setText(hours 
      + ":" + minutes + ":" + millisUntilFinished/1000); 

    // In line with the suggestion provided by jolivier - make the timer 
    // the slave and update its display every time the chronometer 
    // ticks   
    mChronometer 
      .setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() { 

       @Override 
       public void onChronometerTick(Chronometer chronometer) { 

        // Update the display for the chronometer 
        CharSequence text = chronometer.getText();     
        chronometer.setText(text); 

        // Update the display for the timer     
        // !!! BUG !!! 
        // Looks like onChronometerTick is called at the 0th second 
        // and this causes an off by two error if a count down timer 
        // is being implemented. Fixed it with this hack. There's gotta 
        // be a more elegant solution, though. 
        if(counter >= 2) { 
         millisUntilFinished1 = millisUntilFinished1 - 1000; 
         counter = 2; 
        } 
        counter++; 

        if (millisUntilFinished >= 0) { 

         long seconds = (long) (millisUntilFinished/1000); 
         long minutes = (long) ((millisUntilFinished/1000)/60); 
         long hours = (long) (((millisUntilFinished/1000)/60)/60); 

         // Do some formatting to make seconds, minutes and hours look pretty  

         // Update the timer TextView  
         ((TextView) findViewById(R.id.CountDownTimerTextView)) 
           .setText(hours + ":" + minutes + ":" 
             + seconds); 
         } 
        } 

    }); 
    // Other code 
    ... 
} 
+1

這的確是我的意思,只使用一個線程的時鐘,並讓所有的時間邏輯依賴於它。你的黑客可能來自這樣一個事實:當你在天文臺上調用''start()''時,會調用ChronometerListener''。關於這個監聽器的文檔是非常不完整的,你的代碼每秒鐘都會調用它,但是它可能會被更頻繁地調用,你可能想在這個代碼被調用的時候在代碼之外進行測試。 – jolivier

+0

哇!謝謝。我很感激你的建議,並且很高興實現或多或少是你的意思。你說'onChronometerTick' - 它看起來像被稱爲兩次 - 但只有當'mChronometer.start()'被調用時。之後,似乎每秒鐘只調用一次。奇怪。我不知道這個問題是一個優雅的解決方案。 – aLearner

+0

我開始了一個新線程,專注於在這裏遇到的兩個錯誤。它看起來像'onChronometerTick'可能是責備。欲瞭解更多詳情,請參閱http://stackoverflow.com/questions/13523804/onchronometertick-called-two-times-before-chronometer-value-changes – aLearner

2

您可以檢索當前時間與System.currentTimeMillis的(),它存儲到一個變量和將其轉發到mChronometermCounter,以便他們使用相同的時間參考,儘管他們的任務在不同的時間開始。

編輯:與給定的類型,android documentation about Chronometer會告訴你,你可以使用elapsedRealTime來實現我所說的。 CountDownTimer沒有這個,它的start方法是最終的,所以你可能想要使用另一個實現,更好地查看你的用例可能對我們有幫助。

基本上,想讓兩個線程在同一個毫秒內執行一個動作永遠不是一個好主意,其中一個將作爲時鐘,另一個將成爲一個從屬並監聽時鐘。

+0

謝謝你的回答。當你說存儲在變量中的值需要被轉發到「m計時器」和「mCounter」時,我不確定遵循了你的意思。我查看了'mChronometer.start()'和'mCounter.start()'方法,但看起來他們不接受一個初始值(這可以通過使用'System.currentTimeMillis()'得到並存儲那裏)。換句話說,沒有什麼像'mChronometer.start(initialVal)'或'mCounter.start(initialVal)'。或者我誤解了你的建議? – aLearner

+0

你可以在你的問題中給出mChronometer和mCounter的類型嗎?我需要它來幫助你 – jolivier

+0

@ njzk2只是增加了一些代碼來說明事情。謝謝。 – aLearner

相關問題