安卓中的 SeekBar 和媒体播放器
Posted
技术标签:
【中文标题】安卓中的 SeekBar 和媒体播放器【英文标题】:SeekBar and media player in android 【发布时间】:2013-06-14 15:05:04 【问题描述】:我有一个简单的播放器和录音机。一切都很好,但有一个问题。我想添加搜索栏以查看播放记录的进度,并使用此搜索栏设置播放器应该播放的位置。我有 onProgress 但没有效果。这是代码:
package com.example.recorder;
import java.io.IOException;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
public class MainActivity extends Activity implements OnSeekBarChangeListener
private static final String LOG_TAG = "AudioRecordTest";
private static String mFileName = null;
private SeekBar seekBar;
private MediaRecorder mRecorder = null;
private Button startRecord, startPlaying, stopPlaying;
private MediaPlayer mPlayer = null;
private void onRecord(boolean start)
if (start)
startRecording();
else
stopRecording();
private void startPlaying()
if(mPlayer != null && mPlayer.isPlaying())
mPlayer.pause();
else if(mPlayer != null)
mPlayer.start();
else
mPlayer = new MediaPlayer();
try
mPlayer.setDataSource(mFileName);
mPlayer.prepare();
mPlayer.start();
catch (IOException e)
Log.e(LOG_TAG, "prepare() failed");
private void stopPlaying()
mPlayer.release();
mPlayer = null;
startPlaying.setText("Start playing");
private void pausePlaying()
if(mPlayer.isPlaying())
mPlayer.pause();
else
mPlayer.start();
private void startRecording()
mRecorder = new MediaRecorder();
mRecorder.setAudiosource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setOutputFile(mFileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try
mRecorder.prepare();
catch (IOException e)
Log.e(LOG_TAG, "prepare() failed");
mRecorder.start();
private void stopRecording()
mRecorder.stop();
mRecorder.release();
mRecorder = null;
public MainActivity()
mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
mFileName += "/audiorecordtest.3gp";
@Override
public void onCreate(Bundle icicle)
super.onCreate(icicle);
setContentView(R.layout.activity_main);
startPlaying = (Button) findViewById(R.id.buttonStartPlay);
stopPlaying = (Button) findViewById(R.id.buttonStopPlaying);
startRecord = (Button) findViewById(R.id.buttonStartRecord);
seekBar = (SeekBar) findViewById(R.id.seekBar);
startRecord.setOnClickListener(new OnClickListener()
boolean mStartRecording = true;
@Override
public void onClick(View v)
onRecord(mStartRecording);
if (mStartRecording)
startRecord.setText("Stop recording");
else
startRecord.setText("Start recording");
mStartRecording = !mStartRecording;
);
startPlaying.setOnClickListener(new OnClickListener()
boolean mStartPlaying = true;
@Override
public void onClick(View v)
//onPlay(mStartPlaying);
startPlaying();
if (mStartPlaying)
startPlaying.setText("Stop playing");
else
startPlaying.setText("Start playing");
mStartPlaying = !mStartPlaying;
);
stopPlaying.setOnClickListener(new OnClickListener()
boolean mStartPlaying = true;
@Override
public void onClick(View v)
stopPlaying();
);
@Override
public void onPause()
super.onPause();
if (mRecorder != null)
mRecorder.release();
mRecorder = null;
if (mPlayer != null)
mPlayer.release();
mPlayer = null;
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
if(fromUser)
mPlayer.seekTo(progress);
seekBar.setProgress(progress);
else
// the event was fired from code and you shouldn't call player.seekTo()
@Override
public void onStartTrackingTouch(SeekBar seekBar)
// TODO Auto-generated method stub
@Override
public void onStopTrackingTouch(SeekBar seekBar)
// TODO Auto-generated method stub
任何想法应该如何使用搜索栏查看进度并设置记录位置?
【问题讨论】:
从onProgressChanged()
它的冗余中删除`seekBar.setProgress(progress);`。
您的评论不会改变任何事情:)
我刚刚发现,它没用。它只是建议没有回答:)
【参考方案1】:
要在 SeekBar
和 MediaPlayer
之间创建“连接”,您首先需要获取当前录制的最长持续时间并将其设置到搜索栏。
mSeekBar.setMax(mFileDuration/1000); // where mFileDuration is mMediaPlayer.getDuration();
在初始化 MediaPlayer
并例如按下播放按钮后,您应该创建处理程序并发布可运行文件,以便您可以使用 MediaPlayer
的当前位置更新您的 SeekBar
(在 UI thread
本身中)这个:
private Handler mHandler = new Handler();
//Make sure you update Seekbar on UI thread
MainActivity.this.runOnUiThread(new Runnable()
@Override
public void run()
if(mMediaPlayer != null)
int mCurrentPosition = mMediaPlayer.getCurrentPosition() / 1000;
mSeekBar.setProgress(mCurrentPosition);
mHandler.postDelayed(this, 1000);
);
并每秒更新该值。
如果您需要在用户拖动您的SeekBar
时更新MediaPlayer
的位置,您应该将OnSeekBarChangeListener
添加到您的SeekBar
并在那里执行:
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
@Override
public void onStopTrackingTouch(SeekBar seekBar)
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
if(mMediaPlayer != null && fromUser)
mMediaPlayer.seekTo(progress * 1000);
);
这应该可以解决问题! :)
编辑: 我在您的代码中注意到的一件事,不要这样做:
public MainActivity()
mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
mFileName += "/audiorecordtest.3gp";
在您的 onCreate();
中进行所有初始化,不要创建您的 Activity
的构造函数。
【讨论】:
只是为了概念上的理解,为什么不在onCreate
中创建构造函数并进行初始化呢?你听起来构造函数会出故障吗?
是的,最好的方法。请注意:如果您执行 int mCurrentPosition = mMediaPlayer.getCurrentPosition() / 1000;那么你可能也必须这样做:mSeekBar.setMax(mFileDuration / 1000);
这条线真的让我从if(mMediaPlayer != null && fromUser)
之前的滞后问题中解脱出来,非常感谢+1
如何强调验证progressChange 是否由用户触发的重要性。否则,每次调用 setProgress 时,都会每秒调用一次 onProgressChanged 回调,重置搜索位置。谢谢!
还记得在媒体播放器暂停或到达音频文件结尾时删除 runnable,否则 runnable 将继续运行,意识到这一点是因为我每次运行 runnable 时都会记录,并获得大量日志。不过感谢您的解决方案,除此之外它工作得很好【参考方案2】:
我已经成功地使用了这个教程,它真的很容易理解: www.androidhive.info/2012/03/android-building-audio-player-tutorial/
这是有趣的部分:
/**
* Update timer on seekbar
* */
public void updateProgressBar()
mHandler.postDelayed(mUpdateTimeTask, 100);
/**
* Background Runnable thread
* */
private Runnable mUpdateTimeTask = new Runnable()
public void run()
long totalDuration = mp.getDuration();
long currentDuration = mp.getCurrentPosition();
// Displaying Total Duration time
songTotalDurationLabel.setText(""+utils.milliSecondsToTimer(totalDuration));
// Displaying time completed playing
songCurrentDurationLabel.setText(""+utils.milliSecondsToTimer(currentDuration));
// Updating progress bar
int progress = (int)(utils.getProgressPercentage(currentDuration, totalDuration));
//Log.d("Progress", ""+progress);
songProgressBar.setProgress(progress);
// Running this thread after 100 milliseconds
mHandler.postDelayed(this, 100);
;
/**
*
* */
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch)
/**
* When user starts moving the progress handler
* */
@Override
public void onStartTrackingTouch(SeekBar seekBar)
// remove message Handler from updating progress bar
mHandler.removeCallbacks(mUpdateTimeTask);
/**
* When user stops moving the progress hanlder
* */
@Override
public void onStopTrackingTouch(SeekBar seekBar)
mHandler.removeCallbacks(mUpdateTimeTask);
int totalDuration = mp.getDuration();
int currentPosition = utils.progressToTimer(seekBar.getProgress(), totalDuration);
// forward or backward to certain seconds
mp.seekTo(currentPosition);
// update timer progress again
updateProgressBar();
【讨论】:
【参考方案3】:初始化MediaPlayer
和SeekBar
后,您可以这样做:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask()
@Override
public void run()
mSeekBar.setProgress(mMediaPlayer.getCurrentPosition());
,0,1000);
这会更新 SeekBar
每秒(1000 毫秒)
对于更新MediaPlayer
,如果用户拖动SeekBar
,您必须将OnSeekBarChangeListener
添加到您的SeekBar
:
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b)
mMediaPlayer.seekTo(i);
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onStopTrackingTouch(SeekBar seekBar)
);
编码愉快!!!
【讨论】:
【参考方案4】:Kotlin 中的代码:
var updateSongTime = object : Runnable
override fun run()
val getCurrent = mediaPlayer?.currentPosition
startTimeText?.setText(String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(getCurrent?.toLong() as Long),
TimeUnit.MILLISECONDS.toSeconds(getCurrent?.toLong()) -
TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(getCurrent?.toLong()))))
seekBar?.setProgress(getCurrent?.toInt() as Int)
Handler().postDelayed(this, 1000)
用于每秒更改媒体播放器音频文件
如果用户拖动搜索栏,则可以使用以下代码 sn-p
Statified.seekBar?.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean)
if(b && Statified.mediaPlayer != null)
Statified.mediaPlayer?.seekTo(i)
override fun onStartTrackingTouch(seekBar: SeekBar)
override fun onStopTrackingTouch(seekBar: SeekBar)
)
【讨论】:
【参考方案5】:检查这个,你应该以毫秒为单位给出参数,不要只发送progress
到seekTo(int)
还要检查getCurrentPostion() 和getDuration()。
您可以进行一些计算,即,以毫秒为单位转换 progress
,如 msce = (progress/100)*getDuration()
,然后执行 seekTo(msec)
否则我有一个简单的想法,您无需更改任何代码,否则只需在准备好媒体播放器后添加seekBar.setMax(mPlayer.getDuration())
。
这里是你想要的链接seek bar update
【讨论】:
【参考方案6】:下面的代码对我有用。
我已经为 seekbar 创建了一个方法
@Override
public void onPrepared(MediaPlayer mediaPlayer)
mp.start();
getDurationTimer();
getSeekBarStatus();
//Creating duration time method
public void getDurationTimer()
final long minutes=(mSongDuration/1000)/60;
final int seconds= (int) ((mSongDuration/1000)%60);
SongMaxLength.setText(minutes+ ":"+seconds);
//creating a method for seekBar progress
public void getSeekBarStatus()
new Thread(new Runnable()
@Override
public void run()
// mp is your MediaPlayer
// progress is your ProgressBar
int currentPosition = 0;
int total = mp.getDuration();
seekBar.setMax(total);
while (mp != null && currentPosition < total)
try
Thread.sleep(1000);
currentPosition = mp.getCurrentPosition();
catch (InterruptedException e)
return;
seekBar.setProgress(currentPosition);
).start();
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
int progress=0;
@Override
public void onProgressChanged(final SeekBar seekBar, int ProgressValue, boolean fromUser)
if (fromUser)
mp.seekTo(ProgressValue);//if user drags the seekbar, it gets the position and updates in textView.
final long mMinutes=(ProgressValue/1000)/60;//converting into minutes
final int mSeconds=((ProgressValue/1000)%60);//converting into seconds
SongProgress.setText(mMinutes+":"+mSeconds);
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onStopTrackingTouch(SeekBar seekBar)
);
SongProgress 和 SongMaxLength 是显示歌曲时长和歌曲长度的 TextView。
【讨论】:
【参考方案7】: int pos = 0;
yourSeekBar.setMax(mPlayer.getDuration());
在你开始你的MediaPlayer
即mplayer.start()
之后
试试这个代码
while(mPlayer!=null)
try
Thread.sleep(1000);
pos = mPlayer.getCurrentPosition();
catch (Exception e)
//show exception in LogCat
yourSeekBar.setProgress(pos);
在添加此代码之前,您必须为 SeekBar
创建 xml
资源,并在您的 Activity
类的 onCreate()
方法中使用它。
【讨论】:
【参考方案8】:这对我有用:
seekbarPlayer.setMax(mp.getDuration());
getActivity().runOnUiThread(new Runnable()
@Override
public void run()
if(mp != null)
seekbarPlayer.setProgress(mp.getCurrentPosition());
mHandler.postDelayed(this, 1000);
);
【讨论】:
【参考方案9】:鉴于答案hardartcore 对我有用,但在更改之前没有用:
private Handler mHandler = new Handler();
MusicPlayer.this.runOnUiThread(new Runnable()
@Override
public void run()
if(player != null)
int mCurrentPosition = player.getCurrentPosition();//clear ' /1000 '
seekBar.setProgress(mCurrentPosition);
mHandler.postDelayed(this, 1000);
);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
@Override
public void onStopTrackingTouch(SeekBar seekBar)
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
if(player != null && fromUser)
player.seekTo(progress); // clear ' * 1000 '
);
【讨论】:
【参考方案10】:我的代码sn-p:
public class PlayerActivity extends AppCompatActivity
private static final String TAG = "PlayerActivity";
private ActivityPlayerBinding binding;
private MediaPlayer mediaPlayer;
private boolean playingAudio = false;
Runnable runnable;
Handler handler;
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
binding = ActivityPlayerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
try
mediaPlayer.setDataSource(songUrl);
mediaPlayer.prepareAsync();
catch (IOException e)
e.printStackTrace();
mediaPlayer.setOnPreparedListener(mp ->
binding.playBtn.setBackground(getDrawable(R.drawable.ic_pause));
binding.seekbarPlayer.setMax(mp.getDuration());
mediaPlayer.start();
playingAudio = true;
updateSeekbar();
);
mediaPlayer.setOnBufferingUpdateListener((mp, percent) ->
double ratio = percent / 100.0;
int bufferingLevel = (int) (mp.getDuration() * ratio);
binding.seekbarPlayer.setSecondaryProgress(bufferingLevel);
);
binding.seekbarPlayer.setOnSeekBarChangeListener(seekBarChangeListener);
private SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener()
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
if (fromUser)
mediaPlayer.seekTo(progress);
seekBar.setProgress(progress);
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onStopTrackingTouch(SeekBar seekBar)
;
private void updateSeekbar()
try
if (mediaPlayer != null)
int currentPos = mediaPlayer.getCurrentPosition();
binding.seekbarPlayer.setProgress(currentPos);
runnable = this::updateSeekbar;
handler.postDelayed(runnable, 1000);
catch (Exception e)
e.printStackTrace();
Log.d(TAG, "updateSeekbar: " + e.getMessage());
【讨论】:
【参考方案11】:补充@hardartcore 的答案。
最好的方法是在播放期间从 MediaPlayer
获取回调,然后根据进度相应地更新 seekBar,而不是在 Handler 上调用 postDelayed。
另外,在OnSeekBarChangeListener
的onStartTrackingTouch(SeekBar seekBar)
处暂停MediaPlayer
,然后在onStopTrackingTouch(SeekBar seekBar)
上重新启动它。
【讨论】:
【参考方案12】:在前面的语句的基础上,为了更好的性能,还可以加一个if条件
if (player.isPlaying()
handler.postDelayed(..., 1000);
【讨论】:
【参考方案13】:试试这个代码:
public class MainActivity extends AppCompatActivity
MediaPlayer mplayer;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//You create MediaPlayer variable ==> set the path and start the audio.
mplayer = MediaPlayer.create(this, R.raw.example);
mplayer.start();
//Find the seek bar by Id (which you have to create in layout)
// Set seekBar max with length of audio
// You need a Timer variable to set progress with position of audio
final SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setMax(mplayer.getDuration());
new Timer().scheduleAtFixedRate(new TimerTask()
@Override
public void run()
seekBar.setProgress(mplayer.getCurrentPosition());
, 0, 1000);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
// Update the progress depending on seek bar
mplayer.seekTo(progress);
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onStopTrackingTouch(SeekBar seekBar)
);
【讨论】:
您可能希望在解决方案中的代码中添加一些上下文,以帮助其他人更有效地理解解决方案。以上是关于安卓中的 SeekBar 和媒体播放器的主要内容,如果未能解决你的问题,请参考以下文章