2012-11-20 42 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

相关问题