You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ViewPager2中音频与图文加载顺序不匹配问题求助

解决ViewPager2滑动切歌时音频与图文顺序不匹配问题

问题根源

当前MusicAdapter中的MediaPlayer是全局成员变量,而ViewPager2会预加载相邻页面,同时ViewHolder存在复用机制。当多个页面绑定数据时,全局的mediaPlayer会被不断覆盖,导致滑动时音频实例与当前页面不对应,出现顺序错乱。而图文直接绑定到ViewHolder的控件,不受全局变量影响,因此保持正确顺序。

修改方案

1. 将MediaPlayer移至ViewHolder内部

让每个ViewHolder持有独立的MediaPlayer实例,确保每个页面的音频逻辑相互隔离:

修改后的MusicAdapter.java:

public class MusicAdapter extends RecyclerView.Adapter<MusicAdapter.MusicViewHolder> {
    private final Context context;
    private final List<AllMusicModel.Data> mMusiclist;

    public MusicAdapter(Context context, List<AllMusicModel.Data> musicItems) {
        this.context = context;
        mMusiclist = musicItems;
    }

    @NonNull
    @Override
    public MusicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MusicViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.music_layout, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull MusicViewHolder holder, int position) {
        holder.setMusicData(mMusiclist.get(position));
    }

    @Override
    public int getItemCount() {
        return mMusiclist.size();
    }

    class MusicViewHolder extends RecyclerView.ViewHolder {
        ImageView mImageView, play_pause_image;
        TextView txtTitle, txtDesc;
        ProgressBar mProgressBar;
        ConstraintLayout play_pause_layout, linearLayout;
        private MediaPlayer mediaPlayer;
        private Handler handler = new Handler();

        public MusicViewHolder(@NonNull View itemView) {
            super(itemView);
            mImageView = itemView.findViewById(R.id.imageView);
            txtTitle = itemView.findViewById(R.id.txtTitle);
            txtDesc = itemView.findViewById(R.id.txtDesc);
            mProgressBar = itemView.findViewById(R.id.progressBar);
            play_pause_layout = itemView.findViewById(R.id.play_pause_layout);
            play_pause_image = itemView.findViewById(R.id.play_pause_image);
            linearLayout = itemView.findViewById(R.id.linearLayout);
        }

        void setMusicData(AllMusicModel.Data musicItem) {
            txtTitle.setText(musicItem.getTitle());
            
            // 重置当前ViewHolder的MediaPlayer
            if (mediaPlayer != null) {
                mediaPlayer.stop();
                mediaPlayer.release();
                mediaPlayer = null;
            }
            play_pause_image.setImageResource(R.drawable.ic_round_pause);
            play_pause_layout.setVisibility(View.INVISIBLE);

            mImageView.setOnClickListener(v -> {
                if (mediaPlayer != null) {
                    if (mediaPlayer.isPlaying()) {
                        mediaPlayer.pause();
                        play_pause_image.setImageResource(R.drawable.ic_round_play);
                        play_pause_layout.setVisibility(View.VISIBLE);
                    } else {
                        mediaPlayer.start();
                        play_pause_image.setImageResource(R.drawable.ic_round_pause);
                        play_pause_layout.setVisibility(View.VISIBLE);
                        handler.removeCallbacksAndMessages(null);
                        handler.postDelayed(() -> play_pause_layout.setVisibility(View.INVISIBLE), 4000);
                    }
                }
            });

            String audioUrl = ApiUrls.Api_LocalHost_Url + musicItem.getAudio();
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            try {
                mediaPlayer.setDataSource(audioUrl);
                mediaPlayer.prepareAsync();
                mediaPlayer.setOnPreparedListener(mp -> {
                    mProgressBar.setVisibility(View.GONE);
                    mp.start();
                });
            } catch (IOException e) {
                e.printStackTrace();
                mProgressBar.setVisibility(View.GONE);
            }
            mediaPlayer.setOnCompletionListener(mp -> mp.start());

            String imageUrl = ApiUrls.Api_LocalHost_Url + musicItem.getImage();
            Glide.with(context)
                    .load(imageUrl)
                    .placeholder(R.drawable.loading)
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .error(R.drawable.loading)
                    .into(mImageView);
        }

        // 供外部调用的停止音频方法
        public void stopAudio() {
            if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
            }
            handler.removeCallbacksAndMessages(null);
        }

        // 释放资源,避免内存泄漏
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            if (mediaPlayer != null) {
                mediaPlayer.release();
                mediaPlayer = null;
            }
        }
    }
}

2. 监听ViewPager2页面切换,停止前一页音频

在MusicFragment中添加页面切换监听,确保切换页面时停止上一页的音频播放:

修改后的MusicFragment.java:

public class MusicFragment extends Fragment {
    View v;
    List<AllMusicModel.Data> musicItems;
    ViewPager2 musicsViewPager;
    private MusicAdapter musicAdapter;
    private int currentPosition = 0;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        v = inflater.inflate(R.layout.fragment_music, container, false);
        musicsViewPager = v.findViewById(R.id.viewPagerVideos);
        musicsViewPager.setUserInputEnabled(true);
        musicItems = new ArrayList<>();
        musicAdapter = new MusicAdapter(getActivity(), musicItems);
        musicsViewPager.setAdapter(musicAdapter);
        
        // 页面切换监听
        musicsViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                if (currentPosition != position) {
                    RecyclerView recyclerView = (RecyclerView) musicsViewPager.getChildAt(0);
                    MusicAdapter.MusicViewHolder prevHolder = (MusicAdapter.MusicViewHolder) recyclerView.findViewHolderForAdapterPosition(currentPosition);
                    if (prevHolder != null) {
                        prevHolder.stopAudio();
                    }
                    currentPosition = position;
                }
            }
        });
        
        call_music_list_api();
        return v;
    }

    // 原有API调用方法保持不变
    private void call_music_list_api() {
        String Token = "Bearer " + Config.getToken(requireContext());
        String APIChannel = "App";
        String AppVersion = BuildConfig.VERSION_NAME;
        Call<AllMusicModel> mCall = RetrofitClient.getAdapter().GetAllMusic(APIChannel, AppVersion, Token);
        mCall.enqueue(new Callback<AllMusicModel>() {
            @SuppressLint("NotifyDataSetChanged")
            @Override
            public void onResponse(@NonNull Call<AllMusicModel> call, @NonNull Response<AllMusicModel> response) {
                if (response.isSuccessful()) {
                    AllMusicModel alldata = response.body();
                    if (alldata != null) {
                        String code = String.valueOf(alldata.getStatuscode());
                        if (code.equals("200")) {
                            musicItems.clear();
                            musicItems.addAll(alldata.getData());
                            musicAdapter.notifyDataSetChanged();
                        } else if (Objects.equals(code, "401")) {
                            Log.e("MusicFragment", "onResponse: " + alldata.getStatus());
                        }
                    }
                } else {
                    Toast.makeText(getContext(), "Something went wrong", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(@NonNull Call<AllMusicModel> call, @NonNull Throwable t) {
                Log.e("Error", String.valueOf(t));
            }
        });
    }
}

关键优化点

  • 每个ViewHolder独立持有MediaPlayer,彻底避免全局变量被覆盖导致的音频错乱
  • 页面切换时主动停止前一页音频,确保同一时间只有一个音频播放
  • 优化MediaPlayer的资源释放逻辑,避免内存泄漏

内容的提问来源于stack exchange,提问作者sahil gill

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 03:01:14