Android:简单的时间计数器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11730902/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android: simple time counter
提问by DavidNg
I have a simple program with one TextView and two Buttons: Button1 and Button2.
我有一个简单的程序,其中包含一个 TextView 和两个按钮:Button1 和 Button2。
Clicking on Button 1 will start a counter, increasing by 1 every 1 second and show result on TextView; clinking on Button 2 will stop it. Here is a part of my code for Button1. But it does not work.
单击按钮 1 将启动一个计数器,每 1 秒增加 1 并在 TextView 上显示结果;碰上按钮 2 将停止它。这是我的 Button1 代码的一部分。但它不起作用。
Timer T=new Timer();
T.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
myTextView.setText("count="+count);
count++;
}
}, 1000, 1000);
I know that there are some similar questions about this using Thread but it seems like they do not mention about stopping the counter.
我知道使用 Thread 有一些类似的问题,但似乎他们没有提到停止计数器。
Any suggestion is really appreciated.
任何建议都非常感谢。
Added:
添加:
Hello, I just shortened my code just to this from a much bigger program, but it is still crashed:
你好,我刚刚从一个更大的程序中将我的代码缩短到这个,但它仍然崩溃了:
package com.example.hello;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
TextView myTextView;
int count=0;
Timer T;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView=(TextView)findViewById(R.id.t);
T=new Timer();
T.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
myTextView.setText("count="+count);
count++;
}
}, 1000, 1000);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
The log file (I do not want to post this because it is too long but someone requested it):
日志文件(我不想发布它,因为它太长但有人要求它):
07-28 17:35:07.012: W/dalvikvm(11331): threadid=7: thread exiting with uncaught exception (group=0x4001d800)
07-28 17:35:07.016: E/AndroidRuntime(11331): FATAL EXCEPTION: Timer-0
07-28 17:35:07.016: E/AndroidRuntime(11331): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.view.ViewRoot.checkThread(ViewRoot.java:2802)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.view.ViewRoot.requestLayout(ViewRoot.java:594)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.view.View.requestLayout(View.java:8125)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.view.View.requestLayout(View.java:8125)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.view.View.requestLayout(View.java:8125)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.view.View.requestLayout(View.java:8125)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:254)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.view.View.requestLayout(View.java:8125)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.widget.TextView.checkForRelayout(TextView.java:5378)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.widget.TextView.setText(TextView.java:2688)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.widget.TextView.setText(TextView.java:2556)
07-28 17:35:07.016: E/AndroidRuntime(11331): at android.widget.TextView.setText(TextView.java:2531)
07-28 17:35:07.016: E/AndroidRuntime(11331): at com.example.hello.MainActivity.run(MainActivity.java:21)
07-28 17:35:07.016: E/AndroidRuntime(11331): at java.util.Timer$TimerImpl.run(Timer.java:289)
回答by Pavel Dudka
You can introduce flag. Something like isPaused
. Trigger this flag whenever 'Pause' button pressed. Check flag value in your timer task. Success.
您可以引入标志。类似的东西isPaused
。每当按下“暂停”按钮时触发此标志。检查计时器任务中的标志值。成功。
Timer T=new Timer();
T.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable()
{
@Override
public void run()
{
myTextView.setText("count="+count);
count++;
}
});
}
}, 1000, 1000);
onClick(View v)
{
//this is 'Pause' button click listener
T.cancel();
}
回答by hovanessyan
Chekout thisexample, that uses the Chronometerclass: http://android-er.blogspot.com/2010/06/android-chronometer.html
Chekout这个例子,它使用Chronometer类:http: //android-er.blogspot.com/2010/06/android-chronometer.html
Using the Chronometer class will save you managing the thread yourself.
使用 Chronometer 类将节省您自己管理线程的时间。
回答by Giedrius ?likas
Perfect solution if you want to display running clock.
如果你想显示运行时钟,完美的解决方案。
private void startClock(){
Thread t = new Thread() {
@Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
@Override
public void run() {
Calendar c = Calendar.getInstance();
int hours = c.get(Calendar.HOUR_OF_DAY);
int minutes = c.get(Calendar.MINUTE);
int seconds = c.get(Calendar.SECOND);
String curTime = String.format("%02d %02d %02d", hours, minutes, seconds);
clock.setText(curTime); //change clock to your textview
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
}
回答by Andy
Pavel is on the right track with stopping it, except his answer is a waste since it doesn't stop the Timer. Do this on the part he posted with the added else statement:
Pavel 是在正确的轨道上停止它,但他的回答是浪费,因为它没有停止计时器。在他发布的带有添加 else 语句的部分执行此操作:
if(!isPaused)
{
myTextView.setText("count="+count);
count++;
} else {
cancel();//adding this makes it actually stop
}
Oh and you said it crashed. We have no idea why it's crashing, so unfortunately we cannot really help figuring out why its doing that unless we see more code and the error.
哦,你说它崩溃了。我们不知道它为什么会崩溃,所以不幸的是,除非我们看到更多的代码和错误,否则我们无法真正帮助弄清楚它为什么会崩溃。
Good catch @yorkw The problem is obviously with something its trying to do when its starts running. Since its says "Timer-0", this means it never starts officially and it gets stuck in that part. First rule of running threads, NEVER CHANGE UI ELEMENTS FROM OUTSIDE THE UI. I would suggest using runOnUiThread
like york said. Here's a link on how to use it: runOnUiThread.
Pretty sure TimerTask
is a runnable meaning it runs on a separate thread. Least thats what I noticed on the docs.
很好的捕获@yorkw 问题显然在于它开始运行时试图做的事情。因为它说“Timer-0”,这意味着它永远不会正式启动,并且卡在那个部分。运行线程的第一条规则,永远不要从 UI 外部更改 UI 元素。我建议runOnUiThread
像约克说的那样使用。这是有关如何使用它的链接:runOnUiThread。很确定TimerTask
是可运行的,这意味着它在单独的线程上运行。至少这就是我在文档中注意到的。
回答by Gean Carlos Brand?o
This way it is much simpler. It is a feature made available on the android developer site. Link
这样就简单多了。这是 android 开发者网站上提供的一项功能。关联
public void initCountDownTimer(int time) {
new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
textView.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
textView.setText("done!");
}
}.start();
}
回答by thando
As @hovanessyansuggested: You can use the Chronometerclass. E.g., as follows (with Kotlin, Java analog):
正如@hovanessyan建议的那样:您可以使用Chronometer类。例如,如下(使用 Kotlin,Java 模拟):
class ChronometerToggler: Fragment() {
private lateinit var chronometer: Chronometer
private var isChronometerRunning = false
private lateinit var toggleButton: FloatingActionButton
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.my_layout, container, false)
chronometer = view.findViewById(R.id.chronometer)
toggleButton = view.findViewById(R.id.toggle_button)
toggleButton.setOnClickListener {
onToggleButtonPressed()
}
return view
}
private fun onToggleButtonPressed() {
isChronometerRunning = !isChronometerRunning
if (isChronometerRunning) {
chronometer.base = SystemClock.elapsedRealtime()
chronometer.start()
}
else {
chronometer.stop()
}
}
}
In the my_layout.xml
:
在my_layout.xml
:
[...]
<Chronometer android:id="@+id/chronometer" [...]/>
[...]