从片段中的适配器调用更新搜索栏时,MediaPlayer 跳过音频
MediaPlayer skipping audio when updating seekbar from within Adapter call in Fragment
我有一个 MediaPlayer,它包含在自定义 ViewHolder 中,由 RecyclerViewAdapter 创建,而 RecyclerViewAdapter 是由 Fragment 运行 创建的。我正在尝试每秒更新搜索栏以显示 MediaPlayer 正在播放的音频的进度,使用 this question's answer:
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);
}
});
但是,由于我是 运行 从一个片段中将它连接到一个适配器中,而不是直接在 activity 中,我不能使用这一行:
MainActivity.this.runOnUiThread(new Runnable() {
因此,我使用适配器的构造函数将 getActivity()
从片段传递到 RecyclerViewAdapter 并将其设置为全局变量 parentActivity
.
然后我创建了代码来更新 RecyclerViewAdapter 的 onBindViewHolder() 中的搜索栏,如下所示:
final Handler mHandler = new Handler();
this.parentActivity.runOnUiThread(new Runnable(){
@Override
public void run(){
if (mMediaPlayer != null){
int mCurrentPosition = mMediaPlayer.getCurrentPosition();
myViewHolder.seekBar.setProgress(mCurrentPosition);
}
mHandler.postDelayed(this, 1000);
}
});
我的问题是,现在当我播放音频时,虽然搜索栏正确更新,但音频会短暂暂停,或每秒 "skips"。是什么原因造成的,我该如何解决?
编辑:每个 ViewHolder 都有自己的 SeekBar,并且 mMediaPlayer
在适配器中定义为全局变量。
我猜想跳过和暂停的事情是由于过多地使用 main thred 造成的,更重要的是你的 activity 有多个实例。如果将它复制到 Recycler 的每个 ViewHolder,就会发生这种情况。
实际上这是一个非常糟糕的模式,应该避免。
很难为您提供解决方案,因为您没有明确说明您的应用程序究竟是什么样子(例如,每个 ViewHolder 中是否存在搜索栏等)。但假设它是 'worst' 情况,我建议您将另一个侦听器传递给适配器,它将侦听您的 seekBar 或 ViewHolder(取决于您,实际上这个接口可能更大以用于扩展控制)。 Fragment 会在其内部执行刷新操作。
class Adapter(... startPlayingListener: (SeekBar) -> Unit) {
fun onBindViewHolder() {
whateverButton.setOnClickListener {startPlayingListener.invoke(seekBar)}
}
}
class Fragment(...) {
fun bindAdapter() {
adapter = YourAdapter(..., this::onPlayerStart)
}
fun onPlayerStart(seekBar: SeekBar) {
this.parentActivity.runOnUiThread(
{
if (mMediaPlayer != null){
int mCurrentPosition = mMediaPlayer.getCurrentPosition();
seekBar.setProgress(mCurrentPosition);
}
mHandler.postDelayed(this, 1000);
}
}
}
编辑:
如您所说 - 您的解决方案只有一个要播放的歌曲列表,并且每个项目都有自己的 SeekBar。在这种情况下,我建议您创建一个委托 class 来处理播放音乐的逻辑,并仅将 ViewHolder 用作视图(在其他情况下 - 为您的 MVP 的 Presenter 层创建 class) .下面我发布了或多或少显示这个想法的代码。
public interface YourPlayerView() {
void updateSeekBar(int currentPosition);
}
public interface YourPlayerPresenter {
void startPlaying(YourPlayerView view, trackID or something)
void stopPlaying(YourPlayerView view, trackID or something)
}
class YourPlayer(...) implements YourPlayerPresenter {
private MediaPlayer;
@Override
public void startPlaying(YourPlayerView view, trackId or something) {
//start the track - possibly pass it as an argument of this method
//start your handler calling view.updateSeekBar(...)
}
}
public class ViewHolder(YourPlayerPresenter player) implements YourPlayerView {
void bind(...) {
buttonStart.setOnClickListener(player.startPlaying(this, trackId);
}
@Override
public void updateSeekBar(int currentPosition) {
mSeekBar.setProgress(currentPositition)
}
}
您可能需要在片段中创建 mMediaPlayer
,然后将侦听器作为构造函数参数传递给适配器。调用侦听器后,您可以将媒体播放器参数传递给侦听器,并在片段内执行其余的 mMediaPlayer
操作。
通过这种方式,您也可以访问 getActivity()
上下文
您可以将此侦听器从片段传递给您的适配器
public interface OnMediaPlayerInvoked {
void onSeekBarPosition(your params);
}
然后从您的适配器调用此侦听器并将 your params
传递给侦听器 OnMediaPlayerInvoked
,以便可以在片段
中执行其余操作
我有一个 MediaPlayer,它包含在自定义 ViewHolder 中,由 RecyclerViewAdapter 创建,而 RecyclerViewAdapter 是由 Fragment 运行 创建的。我正在尝试每秒更新搜索栏以显示 MediaPlayer 正在播放的音频的进度,使用 this question's answer:
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);
}
});
但是,由于我是 运行 从一个片段中将它连接到一个适配器中,而不是直接在 activity 中,我不能使用这一行:
MainActivity.this.runOnUiThread(new Runnable() {
因此,我使用适配器的构造函数将 getActivity()
从片段传递到 RecyclerViewAdapter 并将其设置为全局变量 parentActivity
.
然后我创建了代码来更新 RecyclerViewAdapter 的 onBindViewHolder() 中的搜索栏,如下所示:
final Handler mHandler = new Handler();
this.parentActivity.runOnUiThread(new Runnable(){
@Override
public void run(){
if (mMediaPlayer != null){
int mCurrentPosition = mMediaPlayer.getCurrentPosition();
myViewHolder.seekBar.setProgress(mCurrentPosition);
}
mHandler.postDelayed(this, 1000);
}
});
我的问题是,现在当我播放音频时,虽然搜索栏正确更新,但音频会短暂暂停,或每秒 "skips"。是什么原因造成的,我该如何解决?
编辑:每个 ViewHolder 都有自己的 SeekBar,并且 mMediaPlayer
在适配器中定义为全局变量。
我猜想跳过和暂停的事情是由于过多地使用 main thred 造成的,更重要的是你的 activity 有多个实例。如果将它复制到 Recycler 的每个 ViewHolder,就会发生这种情况。
实际上这是一个非常糟糕的模式,应该避免。
很难为您提供解决方案,因为您没有明确说明您的应用程序究竟是什么样子(例如,每个 ViewHolder 中是否存在搜索栏等)。但假设它是 'worst' 情况,我建议您将另一个侦听器传递给适配器,它将侦听您的 seekBar 或 ViewHolder(取决于您,实际上这个接口可能更大以用于扩展控制)。 Fragment 会在其内部执行刷新操作。
class Adapter(... startPlayingListener: (SeekBar) -> Unit) {
fun onBindViewHolder() {
whateverButton.setOnClickListener {startPlayingListener.invoke(seekBar)}
}
}
class Fragment(...) {
fun bindAdapter() {
adapter = YourAdapter(..., this::onPlayerStart)
}
fun onPlayerStart(seekBar: SeekBar) {
this.parentActivity.runOnUiThread(
{
if (mMediaPlayer != null){
int mCurrentPosition = mMediaPlayer.getCurrentPosition();
seekBar.setProgress(mCurrentPosition);
}
mHandler.postDelayed(this, 1000);
}
}
}
编辑:
如您所说 - 您的解决方案只有一个要播放的歌曲列表,并且每个项目都有自己的 SeekBar。在这种情况下,我建议您创建一个委托 class 来处理播放音乐的逻辑,并仅将 ViewHolder 用作视图(在其他情况下 - 为您的 MVP 的 Presenter 层创建 class) .下面我发布了或多或少显示这个想法的代码。
public interface YourPlayerView() {
void updateSeekBar(int currentPosition);
}
public interface YourPlayerPresenter {
void startPlaying(YourPlayerView view, trackID or something)
void stopPlaying(YourPlayerView view, trackID or something)
}
class YourPlayer(...) implements YourPlayerPresenter {
private MediaPlayer;
@Override
public void startPlaying(YourPlayerView view, trackId or something) {
//start the track - possibly pass it as an argument of this method
//start your handler calling view.updateSeekBar(...)
}
}
public class ViewHolder(YourPlayerPresenter player) implements YourPlayerView {
void bind(...) {
buttonStart.setOnClickListener(player.startPlaying(this, trackId);
}
@Override
public void updateSeekBar(int currentPosition) {
mSeekBar.setProgress(currentPositition)
}
}
您可能需要在片段中创建 mMediaPlayer
,然后将侦听器作为构造函数参数传递给适配器。调用侦听器后,您可以将媒体播放器参数传递给侦听器,并在片段内执行其余的 mMediaPlayer
操作。
通过这种方式,您也可以访问 getActivity()
上下文
您可以将此侦听器从片段传递给您的适配器
public interface OnMediaPlayerInvoked {
void onSeekBarPosition(your params);
}
然后从您的适配器调用此侦听器并将 your params
传递给侦听器 OnMediaPlayerInvoked
,以便可以在片段