如何从外部方法访问RecyclerView.ViewHolder中的MediaPlayer实现滑动停止播放
解决方案
第一步:优化ViewHolder内MediaPlayer的生命周期逻辑
原有代码每次调用setData都会新建MediaPlayer实例,ViewHolder复用时旧实例没有释放,会出现内存泄漏、多个音频同时播放的问题,先修改ViewHolder代码:
public class ViewHolder extends RecyclerView.ViewHolder { RoundedImageView imageView; TextView username; ImageButton buttonInfo; MediaPlayer mediaPlayer; public ViewHolder(@NonNull @NotNull View itemView) { super(itemView); imageView = itemView.findViewById(R.id.itemCardStackView); username = itemView.findViewById(R.id.textViewUsername_stackView); buttonInfo = itemView.findViewById(R.id.buttonToInfo_stackView); } void setData(Users data) { // 先释放旧的MediaPlayer实例 releaseMediaPlayer(); mediaPlayer = new MediaPlayer(); if (!data.getAudioIntro().equals("")) { try { mediaPlayer.setDataSource(data.getAudioIntro()); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { booleanCallBack.checkAudio(true); username.setText(data.getUsername()); Glide.with(context).load(data.getAvatar()).into(imageView); } }); mediaPlayer.prepareAsync(); } catch (IOException e) { e.printStackTrace(); } } imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!data.getAudioIntro().equals("")) { if (mediaPlayer.isPlaying()) mediaPlayer.pause(); else { mediaPlayer.start(); } } else { Toast.makeText(context, "No Voice intro", Toast.LENGTH_SHORT).show(); } } }); } // 新增停止播放的公共方法 public void stopPlay() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); // 如需停止后回到进度起点可添加以下代码 // mediaPlayer.seekTo(0); } } // 新增释放MediaPlayer资源的公共方法 public void releaseMediaPlayer() { if (mediaPlayer != null) { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); } mediaPlayer.release(); mediaPlayer = null; } } }
第二步:在Adapter中实现stopMediaplayer方法
根据你的布局场景选择对应实现即可:
场景1:卡片堆叠布局一次仅展示1张可见卡片
在Adapter中添加成员变量记录当前活跃的ViewHolder,后续直接调用其停止方法即可:
public class YourAdapter extends RecyclerView.Adapter<ViewHolder> { // 新增成员变量记录当前活跃的ViewHolder private ViewHolder currentActiveHolder; private List<Users> dataList; // ...原有其他Adapter逻辑不变 @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { // 绑定数据时更新当前活跃的ViewHolder记录 currentActiveHolder = holder; holder.setData(dataList.get(position)); } @Override public void onViewRecycled(@NonNull ViewHolder holder) { super.onViewRecycled(holder); // ViewHolder回收时释放资源 holder.releaseMediaPlayer(); if (currentActiveHolder == holder) { currentActiveHolder = null; } } public void stopMediaplayer() { if (currentActiveHolder != null) { currentActiveHolder.stopPlay(); } } }
场景2:同屏有多个可见卡片
遍历所有已挂载的ViewHolder逐个停止播放即可:
public void stopMediaplayer() { for (int i = 0; i < recyclerView.getChildCount(); i++) { View childView = recyclerView.getChildAt(i); ViewHolder holder = (ViewHolder) recyclerView.getChildViewHolder(childView); holder.stopPlay(); } }
额外优化建议
- Activity销毁时记得调用Adapter的资源释放逻辑,避免MediaPlayer后台继续播放造成内存泄漏
- 建议把ViewHolder内的点击事件通过接口回调抛给Activity处理,避免ViewHolder持有Context引用造成内存泄漏
内容的提问来源于stack exchange,提问作者Lộc Trần Phước
相关产品推荐
相关产品推荐

