MediaPlayer, ProgressBar

11

当播放媒体时,这是更新ProgressBar的正确方法吗?我想MediaPlayer中会有一个回调函数,但我找不到它。

mediaPlayer.start();
final SeekBar progress = (SeekBar) dialog.findViewById(R.id.seekBar1);
progress.setMax(mediaPlayer.getDuration());
new CountDownTimer(mediaPlayer.getDuration(), 250) {
  public void onTick(long millisUntilFinished) {
    progress.setProgress(progress.getProgress() + 250);
  }
  public void onFinish() {}
}.start();

最好的祝福。

3个回答

23

就我个人而言,我会启动一个线程,每隔大约200毫秒检查一次getCurrentPosition(),直到触发了onCompletion()事件为止:

private class MediaObserver implements Runnable {
  private AtomicBoolean stop = new AtomicBoolean(false);

  public void stop() {
    stop.set(true);
  }

  @Override
  public void run() {
    while (!stop.get()) {
      progress.setProgress(mediaPlayer.getCurrentPosition());
      Thread.sleep(200);
    }
  }
}

private MediaObserver observer = null;

public void runMedia() {
  mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener{
    @Override
    public void onCompletion(MediaPlayer mPlayer) {
      observer.stop();
      progress.setProgress(mPlayer.getCurrentPosition());
    }
  });
  observer = new MediaObserver();
  mediaPlayer.start();
  new Thread(observer).start();
}

计时器不会阻塞用户界面,所以我想它在另一个线程中。所以,我想这是相同的。但愿我聪明到足以弄清这些事情。 - pouzzler
@pouzzler 倒计时定时器在另一个线程中运行,唯一的区别是它不使用实际进度。我将编写一个多线程示例。 - JRaymond
你的例子中为什么使用了原子操作来处理布尔值? - tobi.b
在这种情况下,可能没有必要,因为只有观察线程正在操作停止变量,并且它使用纯赋值。但是一般来说,使用原子类型可以防止多个线程同时尝试操作变量的线程冲突,所以这是一个好习惯。 - JRaymond
为了准确起见,MediaPlayer.OnCompletionListener的方法名为onCompletion(),而不是onComplete()。 - matdev
显示剩余3条评论

11

请参考 android.widget.MediaController,它具有进度条。

他们使用一个处理程序来递归调用自身(如果需要),您可以将延迟设置为您希望更新进度条的频率。

请注意,控制器可以显示或隐藏,也可以被用户拖动,当然,视频也可能停止。这些是在更新进度条之前进行各种检查(!mDragging && mShowing && mVideoView.isPlaying())的原因。

protected Handler mHandler = new Handler()
{
    @Override
    public void handleMessage(Message msg)
    {
        int pos;
        switch (msg.what)
        {
            // ...

            case SHOW_PROGRESS:
                pos = setProgress();
                if (!mDragging && mShowing && mVideoView.isPlaying())
                {
                    msg = obtainMessage(SHOW_PROGRESS);
                    sendMessageDelayed(msg, 1000 - (pos % 1000));
                }
                break;

             // ...
        }
    }
};

要开始使用它,使用:

mHandler.sendEmptyMessage(SHOW_PROGRESS);

它会自动停止,但您应该使用以下方式取消最后一个挂起的请求:

mHandler.removeMessages(SHOW_PROGRESS);

1
谢谢,正是我想要的。 - pouzzler
@pouzzler - 不用客气!查看MediaController类对于给你制作自定义控制器的想法很有帮助。 - Peter Ajtai
第二个链接已经因为 DMCA 下架了:https://github.com/OESF/OHA-Android-4.0.1_r1.0 - Andrew Stromme

3
最有效的方法是使用JRaymond的答案和EventBus
private class MediaObserver implements Runnable {
    private AtomicBoolean stop = new AtomicBoolean(false);
    public void stop() {
        stop.set(true);
    }

    @Override public void run() {
        while (!stop.get()) {
            try {
              if (player.isPlaying())
                 sendMsgToUI(player.getCurrentPosition(),
                             player.getDuration());
            } catch (Exception e){e.printStackTrace();}
            try { 
              Thread.sleep(100);
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

private MediaObserver observer = null;

public void runMedia() {
    observer = new MediaObserver();
    new Thread(observer).start();
}

//handles all the background threads things for you
private void sendMsgToUI(int position) {
    ChangingEvent event = new ChangingEvent(position);
    EventBus.getDefault().post(event);
}

ChangingEvent类的代码大概是这样的:
public class ChangingEvent {
  private int position;

  public ChangingEvent(int position) {
      this.position= position;
  }

  public int getPosition() {
      return position;
  }
}

在您的Activity或Fragment中,您只需要这样做:
class YouClass extends Activity {
  private EventBus eventBus = EventBus.getDefault();
}

@Override protected void onCreate(Bundle savedInstanceState) {
    //your code
    eventBus.register(this);
}
//eventbus that updates UI
public void onEventMainThread(ChangingEvent event) {
    //seekbar or any other ui element
    seekBar.setProgress(event.getPosition());
}

我喜欢这种方法,但我认为你忘记了添加循环/while,使得run()被执行不止一次。 - M Penades
哦,是的,谢谢你注意到了 - 我现在会纠正它。 - Fedor Tsyganov

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