私はレコーダーとプレーヤーを備えたアプリに取り組んでいます。記録された.wavファイルを再生するためにmediaplayerを使用していますが、その間にシークバーに更新したいと考えています。すべてが正常に動作していますが、私の問題は、シークバーへのメディアプレーヤーの進行状況の更新がスムーズに行われていないことです。小さなファイルを再生している場合、シークバーの親指が数秒または間でジャンプします。
誰かがシークバーの進行状況をスムーズに求めるための回避策を教えてくれますか?私のコードを以下に示します。
mediaPlayerIntiate();
mediaPlayerSetSource();
mMediaPlayer.start();
task = new TimerTask() {
@Override
public void run() {
Graphbar.post(new Runnable() {
@Override
public void run() {
if (mMediaPlayer != null) {
if (playButtonState == MediaMode.PLAY) {
if (mMediaPlayer.isPlaying()) {
Graphbar.setProgress(mMediaPlayer
.getCurrentPosition());
mediaPlayerUpdateTimer(mMediaPlayer
.getCurrentPosition());
enableRewindandForward();
}
}
}
}
});
}
};
timer = new Timer();
timer.schedule(task, 0, 8);
mMediaPlayer.getCurrentPosition()現在の時間をミリ秒で返し、これを最大容量が100であるSeekbarに更新します。ファイルの長さと100で1つの数式を作成します。この関数を試してください
MediaPlayer mMediaPlayer = new MediaPlayer();
final SeekBar mSeelBar = new SeekBar(this);
final int duration = mMediaPlayer.getDuration();
final int amoungToupdate = duration / 100;
Timer mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!(amoungToupdate * mSeelBar.getProgress() >= duration)) {
int p = mSeelBar.getProgress();
p += 1;
mSeelBar.setProgress(p);
}
}
});
};
}, amoungToupdate);
このプロセスは、メディアプレーヤーが再生を開始するときに呼び出す必要があります。内部
mediaPlayer.setOnPreparedListener(new OnPreparedListener(){
@Override
public void onPrepared(MediaPlayer mp) {
**// call here**
});
更新
秒単位で125回の更新は、行うべきではありません。 SeekBarを更新する間隔を増やしてください。 NullPointerのコメントを読んだ後、これを追加します
seekbar.setProgress()はintのみを受け入れます。したがって、私たちのほとんどは、経過した割合をこのメソッドに渡す傾向があります。ただし、よりスムーズな進行が必要な場合は、ミリ秒単位の期間をMAXとして使用できます。次に、シークバーの進行をミリ秒ごとに更新します。以下は例であり、Androidの携帯電話には60 fps(フレーム/秒)のリフレッシュレートが付属しているため、ほぼ毎回、15ミリ秒ごとに更新しています。
try{
mediaPlayer.start();
seekbar.setProgress(0);
seekbar.setMax(mediaPlayer.getDuration());
// Updating progress bar
seekHandler.postDelayed(updateSeekBar, 15);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
/**
* Background Runnable thread
* */
private Runnable updateSeekBar = new Runnable() {
public void run() {
long totalDuration = mediaPlayer.getDuration();
long currentDuration = mediaPlayer.getCurrentPosition();
// Displaying Total Duration time
remaining.setText(""+ milliSecondsToTimer(totalDuration-currentDuration));
// Displaying time completed playing
elapsed.setText(""+ milliSecondsToTimer(currentDuration));
// Updating progress bar
seekbar.setProgress((int)currentDuration);
// Call this thread again after 15 milliseconds => ~ 1000/60fps
seekHandler.postDelayed(this, 15);
}
};
/**
* Function to convert milliseconds time to
* Timer Format
* Hours:Minutes:Seconds
* */
public String milliSecondsToTimer(long milliseconds){
String finalTimerString = "";
String secondsString = "";
// Convert total duration into time
int hours = (int)( milliseconds / (1000*60*60));
int minutes = (int)(milliseconds % (1000*60*60)) / (1000*60);
int seconds = (int) ((milliseconds % (1000*60*60)) % (1000*60) / 1000);
// Add hours if there
if(hours > 0){
finalTimerString = hours + ":";
}
// Prepending 0 to seconds if it is one digit
if(seconds < 10) {
secondsString = "0" + seconds;
}else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutes + ":" + secondsString;
// return timer string
return finalTimerString;
}
player.prepare(); // or start()
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleWithFixedDelay(new Runnable()
{
@Override
public void run()
{
progressBar.setProgress(player.getCurrentPosition());
}
}, 1, 1, TimeUnit.MICROSECONDS);
これがシークバーの処理方法です。
mediaPlayer.setOnPreparedListener(new OnPreparedListener(){
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
new SeekBarHandler().execute();
});
これで、次のようにシークバーを処理するSeekBarHandlerという非同期タスクがあります。
public class SeekBarHandler extends AsyncTask<Void, Void, Void> {
@Override
protected void onPostExecute(Void result) {
Log.d("##########Seek Bar Handler ################","###################Destroyed##################");
super.onPostExecute(result);
}
@Override
protected void onProgressUpdate(Void... values) {
seekBar.setProgress(mediaPlayer.getCurrentPosition());
super.onProgressUpdate(values);
}
@Override
protected Void doInBackground(Void... arg0) {
while(mediaPlayer.isPlaying()&&isViewOn==true) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
onProgressUpdate();
}
return null;
}
}
OnPauseで、ユーザーがシークバーを表示できない場合にスレッドを継続することは意味がないため、AsyncTaskを終了します。
protected void onPause() {
isViewOn=false;
super.onPause();
}
そしてonResumeで私はこのようにAsyncTaskAgainを開始します
protected void onResume() {
isViewOn=true;
new SeekBarHandler().execute();
super.onResume();
}
ご覧のとおり、ブールフラグisViewOnを使用して、ビューがオンになっているかどうかを確認し、シークバーを処理します。
発生している問題は、AndroidのSeekBarの設計/実装方法に関係しています。これは非常にうまく機能しますが、使用されるセグメントの組み合わせ(つまり、seekbar.setMax(int)
)とハンドラーの遅延時間によって制限されます。
そうは言っても、ハンドラの代わりにViewPropertyAnimatorsを使用する独自のSmoothSeekBarを作成するためにSeekBarをサブクラス化しました。
ここで確認してください: https://github.com/Indatus/Android-SmoothSeekBar