如何在Android中每秒更改一个TextView

17

我制作了一个简单的安卓音乐播放器。我想要一个TextView,以分钟:秒的格式显示歌曲当前时间。所以我尝试让activity成为Runnable,在run()方法中加入如下代码:

int position = 0;
while (MPService.getMP() != null && position<MPService.duration) {
try {
    Thread.sleep(1000);
    position = MPService.getSongPosition();
} catch (InterruptedException e) {
    return;
}

// ... convert position to formatted minutes:seconds string ...

currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
但是这种方法失败了,因为我只能在创建TextView的线程中操作它。所以我尝试使用runOnUiThread(),但那也不行,因为它会在主线程上重复调用Thread.sleep(1000),导致活动只停留在一个空白屏幕上。那么有什么好的想法来解决这个问题吗?
private int startTime = 0;
private Handler timeHandler = new Handler();
private Runnable updateTime = new Runnable() {
    public void run() {
        final int start = startTime;
        int millis = appService.getSongPosition() - start;
        int seconds = (int) ((millis / 1000) % 60);
        int minutes = (int) ((millis / 1000) / 60);
        Log.d("seconds",Integer.toString(seconds)); // no problem here
        if (seconds < 10) {
            // this is hit, yet the text never changes from the original value of 0:00
            currentTime.setText(String.format("%d:0%d",minutes,seconds));
        } else {
            currentTime.setText(String.format("%d:%d",minutes,seconds));
        }
        timeHandler.postAtTime(this,(((minutes*60)+seconds+1)*1000));
    }

};

private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
        IBinder rawBinder) {
      appService = ((MPService.LocalBinder)rawBinder).getService();

    // start playing the song, etc. 

    if (startTime == 0) {
        startTime = appService.getSongPosition();
        timeHandler.removeCallbacks(updateTime);
        timeHandler.postDelayed(updateTime,1000);
    }
}
5个回答

13

这个怎么样:

    int delay = 5000; // delay for 5 sec.
    int period = 1000; // repeat every sec.

    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask()
        {
            public void run()
            {
                //your code
            }
        }, delay, period);

你不能通过计时器访问UI,这样会导致未定义。你需要使用Handler或Extend TimerTask,以便它可以接受UI元素作为参数。 - Flash Thunder

10

developer.android.com是任何Android问题的绝佳起点。 :) - MusiGenesis
我知道我把你的答案标记为正确,但是我在实现这个过程中遇到了麻烦... 我没有收到任何异常,它似乎应该正常工作,但它悄无声息地无法设置TextView的文本。如果你想看一下,我已经把新代码发布上去了。 - herpderp
1
我参考了这篇文章并尝试实现,但我使用了 mHandler.postDelayed(mUpdateTimeTask, 1000);而不是 mHandler.postAtTime(this, start + (((minutes * 60) + seconds + 5) * 1000));我从另一篇博客文章中找到了这个解决方案(见下文),它清楚地提供了一个非常好的解决方案。特别是,如果您是后台服务,并希望使用类似计时器的功能定期更新UI。 https://www.websmithing.com/2011/02/01/how-to-update-the-ui-in-an-android-activity-using-data-from-a-background-service/ - Jim C

4
您需要使用处理程序来处理与GUI的交互。具体地,线程不能触及主线程上的任何内容。您在线程中执行某些操作,如果您需要更改主线程中的内容,则调用处理程序并在那里执行。
具体来说,它应该像这样:
线程 t = new Thread(new Runnable(){ ... 在此处执行操作
Handler.postMessage(); });
然后在代码的其他地方,您可以执行以下操作:
Handler h = new Handler(){ 一些操作... 修改UI元素 }
思路就是这样,线程执行操作,通知处理程序,然后处理程序接收此消息并执行诸如更新UI线程上的文本视图之类的操作。

只是挑刺一下,线程不能完全不触碰主线程中的任何东西——SeekBar 可以被触碰。但还是谢谢 :) - herpderp

0

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接