How to make audio player with play/pause button and seekbar in recycleview
Asked Answered
H

2

10

I need some help, I want to make a recycle view containing audio in each item, how to play the audio when press play button and set progress to seekbar and at the same time pause other sound and convert the play button to play mode in all row except the playing one be in pause mode such as whatsapp or chats in facebook messenger

This is my code: MyAdapter.class

    private List<String> positions = new ArrayList<>();

  public myAdapter(Activity activity, ArrayList<Tweet> list) {
    this.activity = activity;
    this.list = list;
    mPlayer = new MediaPlayer();
    }
    public void onBindViewHolder(final viewHolder holder, final int position) {
        if (!positions.contains(String.valueOf(position)))
            positions.add(String.valueOf(position));
        }
        }

viewHolder.class

  public class viewHolder extends RecyclerView.ViewHolder implements
        SeekBar.OnSeekBarChangeListener, View.OnClickListener {
    LinearLayout parentPanel;
    int viewType;
    private RelativeLayout pauseLayout, playLayout;
    private ImageView pauseIcon, playIcon;
    private SeekBar seekBar;
    private TextView periodTime;
    AudioCallbacks mAudioCallbacks;
    private int last_position;

    viewHolder(final View itemView, int viewType) {
        super(itemView);
        this.viewType = viewType;

            playLayout = (RelativeLayout) itemView.findViewById(R.id.play_layout);
            pauseLayout = (RelativeLayout) itemView.findViewById(R.id.pause_layout);
            playIcon = (ImageView) itemView.findViewById(R.id.play_icon);
            pauseIcon = (ImageView) itemView.findViewById(R.id.pause_icon);
            seekBar = (SeekBar) itemView.findViewById(R.id.seekBar);
            periodTime = (TextView) itemView.findViewById(R.id.period_time);


            seekBar.setOnSeekBarChangeListener(this);
            playLayout.setOnClickListener(this);
            pauseLayout.setOnClickListener(this);
            mAudioCallbacks = new AudioCallbacks() {
                @Override
                public void onUpdate(int percentage) {
                    seekBar.setProgress(percentage);
                    if (percentage == 100)
                        mAudioCallbacks.onStop();
                }
                @Override
                public void onPause() {
                    Log.i("on pause audio", " pause");
                }
                @Override
                public void onStop() {
                    Log.i("on stop audio", " stop");
                    stopPlayingAudio();
                }
            };
    }

    void stopPlayingAudio() {
        if (mPlayer != null) {
            if (mPlayer.isPlaying()) {
                updateAudioProgressBar();
                mPlayer.stop();
                mPlayer.reset();
                seekBar.setProgress(0);
                playLayout.setVisibility(View.VISIBLE);
                pauseLayout.setVisibility(View.GONE);
            }
        }
    }

    void pausePlayingAudio() {
        if (mPlayer != null) {
            if (mPlayer.isPlaying()) {
                mPlayer.pause();
                updateAudioProgressBar();
                mAudioCallbacks.onPause();
            }
        }
    }
    void playingAudio(Tweet message) {
        updateAudioProgressBar();
        if (mPlayer != null) {
            try {
                mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mPlayer.setDataSource(message.getAudioUrl());
                mPlayer.prepare();
                mPlayer.start();
                periodTime.setVisibility(View.VISIBLE);
                periodTime.setText(String.valueOf(message.getAudioDuration()));
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    void updateAudioProgressBar() {
        durationHandler.postDelayed(mUpdateTimeTask, 100);
    }
    private Runnable mUpdateTimeTask = new Runnable() {
        public void run() {
            try {
                if (mPlayer.isPlaying()) {
                    long totalDuration = mPlayer.getDuration();
                    long currentDuration = mPlayer.getCurrentPosition();
                    int progress = Utils.getProgressPercentage(currentDuration, totalDuration);
                    mAudioCallbacks.onUpdate(progress);
                    periodTime.setText(Utils.getFileTime(currentDuration));
                    durationHandler.postDelayed(this, 100);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    };

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        durationHandler.removeCallbacks(mUpdateTimeTask);
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        int totalDuration = mPlayer.getDuration();
        int currentPosition = Utils.progressToTimer(seekBar.getProgress(), totalDuration);
        mPlayer.seekTo(currentPosition);
        updateAudioProgressBar();
    }

    @Override
    public void onClick(View view) {
        Log.e("getAdapterPosition()L", last_position + " /");
        for (String pos : positions) {
            if (!pos.equals(String.valueOf(getAdapterPosition())))
                notifyItemChanged(Integer.parseInt(pos));
        }
        last_position = getAdapterPosition();
        Log.e("getAdapterPosition()F", last_position + " /");
        Tweet message = list.get(getAdapterPosition());
        switch (view.getId()) {
            case R.id.pause_layout:
                playLayout.setVisibility(View.VISIBLE);
                pauseLayout.setVisibility(View.GONE);
                pausePlayingAudio();
                break;
            case R.id.play_layout:
                playLayout.setVisibility(View.GONE);
                pauseLayout.setVisibility(View.VISIBLE);
                mAudioCallbacks.onStop();
                playingAudio(message);
                break;
        }
    }
    }
    public interface AudioCallbacks {
    void onUpdate(int percentage);
    void onPause();
    void onStop();
    }

the problem of this code is :

  1. some time the image not reversed of other object when play specific item .
  2. when scrolled the pause button appear in multiple row.
  3. when play one then play another, stop the first and run the new one but when return to the previous not playing.
  4. when leave the activity or press back press button the sound not stopped.

Can someone helped me, please?

Hey answered 11/9, 2017 at 12:42 Comment(0)
P
26

Controlling and updating progress of MediaPlayer from RecyclerView cells is tricky. Your solution does not correctly update the view states in onBindViewHolder call, which is the root cause of all the issues you are facing. Few other notes one should keep in mind are as follows:

  • update cell view state correctly, which also include add/remove progress updater
  • try to avoid anonymous allocations of xxxListeners, Runnables in onBindViewHolder, i.e. ViewHolder can be used to implement xxxListener interfaces etc.
  • remove the seek bar updater, when media player completes the playback of audio
  • respect activity life-cycle and release/stop media player if it is not required to play the audio

Below source shows possible implementation of Adapter

private class AudioItemAdapter extends RecyclerView.Adapter<AudioItemAdapter.AudioItemsViewHolder> {

    private MediaPlayer mediaPlayer;

    private List<AudioItem> audioItems;
    private int currentPlayingPosition;
    private SeekBarUpdater seekBarUpdater;
    private AudioItemsViewHolder playingHolder;

    AudioItemAdapter(List<AudioItem> audioItems) {
        this.audioItems = audioItems;
        this.currentPlayingPosition = -1;
        seekBarUpdater = new SeekBarUpdater();
    }

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

    @Override
    public void onBindViewHolder(AudioItemsViewHolder holder, int position) {
        if (position == currentPlayingPosition) {
            playingHolder = holder;
            updatePlayingView();
        } else {
            updateNonPlayingView(holder);
        }
    }

    @Override
    public void onViewRecycled(AudioItemsViewHolder holder) {
        super.onViewRecycled(holder);
        if (currentPlayingPosition == holder.getAdapterPosition()) {
            updateNonPlayingView(playingHolder);
            playingHolder = null;
        }
    }

    private void updateNonPlayingView(AudioItemsViewHolder holder) {
        holder.sbProgress.removeCallbacks(seekBarUpdater);
        holder.sbProgress.setEnabled(false);
        holder.sbProgress.setProgress(0);
        holder.ivPlayPause.setImageResource(R.drawable.ic_play_arrow);
    }

    private void updatePlayingView() {
        playingHolder.sbProgress.setMax(mediaPlayer.getDuration());
        playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
        playingHolder.sbProgress.setEnabled(true);
        if (mediaPlayer.isPlaying()) {
            playingHolder.sbProgress.postDelayed(seekBarUpdater, 100);
            playingHolder.ivPlayPause.setImageResource(R.drawable.ic_pause);
        } else {
            playingHolder.sbProgress.removeCallbacks(seekBarUpdater);
            playingHolder.ivPlayPause.setImageResource(R.drawable.ic_play_arrow);
        }
    }

    void stopPlayer() {
        if (null != mediaPlayer) {
            releaseMediaPlayer();
        }
    }

    private class SeekBarUpdater implements Runnable {
        @Override
        public void run() {
            if (null != playingHolder) {
                playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
                playingHolder.sbProgress.postDelayed(this, 100);
            }
        }
    }

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

    class AudioItemsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, SeekBar.OnSeekBarChangeListener {
        SeekBar sbProgress;
        ImageView ivPlayPause;

        AudioItemsViewHolder(View itemView) {
            super(itemView);
            ivPlayPause = (ImageView) itemView.findViewById(R.id.ivPlayPause);
            ivPlayPause.setOnClickListener(this);
            sbProgress = (SeekBar) itemView.findViewById(R.id.sbProgress);
            sbProgress.setOnSeekBarChangeListener(this);
        }

        @Override
        public void onClick(View v) {
            if (getAdapterPosition() == currentPlayingPosition) {
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                } else {
                    mediaPlayer.start();
                }
            } else {
                currentPlayingPosition = getAdapterPosition();
                if (mediaPlayer != null) {
                    if (null != playingHolder) {
                        updateNonPlayingView(playingHolder);
                    }
                    mediaPlayer.release();
                }
                playingHolder = this;
                startMediaPlayer(audioItems.get(currentPlayingPosition).audioResId);
            }
            updatePlayingView();
        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (fromUser) {
                mediaPlayer.seekTo(progress);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }
    }

    private void startMediaPlayer(int audioResId) {
        mediaPlayer = MediaPlayer.create(getApplicationContext(), audioResId);
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                releaseMediaPlayer();
            }
        });
        mediaPlayer.start();
    }

    private void releaseMediaPlayer() {
        if (null != playingHolder) {
            updateNonPlayingView(playingHolder);
        }
        mediaPlayer.release();
        mediaPlayer = null;
        currentPlayingPosition = -1;
    }
}

You can find complete working solution here - GitHub

Preeminence answered 13/9, 2017 at 12:2 Comment(5)
its really helpfulShurlocke
Followed the GitHub demo, Really great and helpful, Thanks.Vito
The only problem I found in this is that the seek bar is not updating. And in the in the github one the seek bar won't starts the first time. Any suggestions?Postprandial
@AshutoshSagar Call mediaplayer.prepare() before mediaplayer.start()Gignac
@Preeminence How to save state of an audio if it paused and played another audio.We can save state of an audio and restart from where it stopped after playing another audio in whatsapp chat.Please helpGignac
K
0
    public class MyAdapter2 extends RecyclerView.Adapter<MyAdapter2.AudioItemsViewHolder> {

static MediaPlayer mediaPlayer;
Activity activity;


private final ArrayList<GroupItems> audioItems;//change it() to your items
private int currentPlayingPosition;
private final SeekBarUpdater seekBarUpdater;
private AudioItemsViewHolder playingHolder;



   public MyAdapter2(Activity activity, ArrayList<GroupItems> items_pro) {
    this.audioItems = items_pro;
    this.currentPlayingPosition = -1;
    seekBarUpdater = new SeekBarUpdater();
    this.activity = activity;

}    


@Override
public AudioItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  //put YourItemsLayout;
    return new AudioItemsViewHolder(LayoutInflater.from(parent.getContext()).inflate(YourItemsLayout, parent, false));
}

@Override
public void onBindViewHolder(AudioItemsViewHolder holder, int position) {
if (position == currentPlayingPosition) {
        playingHolder = holder;
        updatePlayingView();
    } else {
        updateNonPlayingView(holder);
    }
}
   private void updateNonPlayingView(AudioItemsViewHolder holder) {
    holder.sbProgress.removeCallbacks(seekBarUpdater);
    holder.sbProgress.setEnabled(false);
    holder.sbProgress.setProgress(0);
    holder.ivPlayPause.setImageResource(R.drawable.ic_baseline_play_arrow_24);
}

private void updatePlayingView() {
    playingHolder.sbProgress.setMax(mediaPlayer.getDuration());
    playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
    playingHolder.sbProgress.setEnabled(true);
    if (mediaPlayer.isPlaying()) {
        playingHolder.sbProgress.postDelayed(seekBarUpdater, 100);
        playingHolder.ivPlayPause.setImageResource(R.drawable.ic_pause);
    } else {
        playingHolder.sbProgress.removeCallbacks(seekBarUpdater);
        playingHolder.ivPlayPause.setImageResource(R.drawable.ic_baseline_play_arrow_24);
    }
}
  private class SeekBarUpdater implements Runnable {
    @Override
    public void run() {
        if (null != playingHolder && null != mediaPlayer) {
            playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
            playingHolder.sbProgress.postDelayed(this, 100);
        }
    }
}

@Override
public int getItemCount() {
    return audioItems.size();
}
 
    class AudioItemsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, SeekBar.OnSeekBarChangeListener {
    SeekBar sbProgress;
    ImageView ivPlayPause;
    AudioItemsViewHolder(View itemView) {
        super(itemView);
     ivPlayPause = itemView.findViewById(R.id.sound_btn);
        ivPlayPause.setOnClickListener(this);
        sbProgress = itemView.findViewById(R.id.seekBar);
        sbProgress.setOnSeekBarChangeListener(this);
    }
    @Override
    public void onClick(View v) {


        switch (v.getId()) {
            case R.id.seekBar:
                break;

            case R.id.sound_btn: {
                if (getAdapterPosition() == currentPlayingPosition) {
                    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                        mediaPlayer.pause();
                    } else {
                        if (mediaPlayer != null)
                            mediaPlayer.start();
                    }
                } else {
                    currentPlayingPosition = getAdapterPosition();
                    if (mediaPlayer != null) {
                        if (null != playingHolder) {
                            updateNonPlayingView(playingHolder);
                        }
                        mediaPlayer.release();
                    }
                    playingHolder = this;



                   
                        PlaySound(YOUR AUDIO FILE);//put your audio file


                }
                if (mediaPlayer != null)
                    updatePlayingView();
            }
            break;
    }


    }


     @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (fromUser) {
            mediaPlayer.seekTo(progress);
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }
}
 private void PlaySound(File filesound) {

    mediaPlayer = MediaPlayer.create(activity, Uri.parse(String.valueOf(filesound)));

    mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            releaseMediaPlayer();
        }
    });
    mediaPlayer.start();
}
    private void releaseMediaPlayer() {
    if (null != playingHolder) {
        updateNonPlayingView(playingHolder);
    }
    if (outputFile.exists())
        outputFile.delete();

    mediaPlayer.release();
    mediaPlayer = null;
    currentPlayingPosition = -1;
}
}
Kamin answered 9/2, 2022 at 6:11 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.