简体   繁体   中英

Multiple SeekBars associated with MediaPlayer in ListView

I have a ListView which displays voice messages (just like WhatsApp's), I'm using BaseAdapter , and my layout contains a play button and a SeekBar . What I want is to play the audio on the click of the play button , and update the SeekBar .

So I implemented this custom SeekBar which updates itself.

SelfUpdatingSeekBar.java :

public class SelfUpdatingSeekBar extends SeekBar {

MediaPlayer mp;
boolean mActive;


public void setMediaPlayer(MediaPlayer mp){
    this.mp = mp;
}


Runnable mUpdate = new Runnable() {
    @Override
    public void run() {
        long totalDuration = mp.getDuration();
        long currentDuration = mp.getCurrentPosition();

        // Updating progress bar
        int progress = (int)(getProgressPercentage(currentDuration, totalDuration));
        setProgress(progress);
        postDelayed(this, 100);
    }
} ;

public void setActive(int progress) {
    if (!mActive) {
        mActive = true;
        removeCallbacks(mUpdate);
        setProgress(progress);
        post(mUpdate);
    }
}

public void setInactive(int progress) {
    if (mActive) {
        mActive = false;
        removeCallbacks(mUpdate);
    }
    setProgress(progress);
}



public SelfUpdatingSeekBar(Context context) {
    super(context);
}

public SelfUpdatingSeekBar(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public SelfUpdatingSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

public SelfUpdatingSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}



public int getProgressPercentage(long currentDuration, long totalDuration){
    Double percentage = (double) 0;

    long currentSeconds = (int) (currentDuration / 1000);
    long totalSeconds = (int) (totalDuration / 1000);

    // calculating percentage
    percentage =(((double)currentSeconds)/totalSeconds)*100;

    // return percentage
    return percentage.intValue();
}

}

This is how my getView() looks like:

public View getView(final int i, View convertView, ViewGroup viewGroup){

    final ViewHolder viewHolder;



    if(convertView == null){
        LayoutInflater inflater = ((MainActivity) context).getLayoutInflater();
        viewHolder = new ViewHolder();
        convertView = inflater.inflate(R.layout.sent_voice_bubble, viewGroup, false);

        viewHolder.sent_voice_seekbar = (SelfUpdatingSeekBar) convertView.findViewById(R.id.sent_seekbar);
        viewHolder.sent_voice_seekbar.setMediaPlayer(mMediaPlayer);
        viewHolder.sent_voice_play = (ImageView) convertView.findViewById(R.id.sent_playAudio);
        viewHolder.sent_voice_container = convertView.findViewById(R.id.sent_vmessagesection);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(((int)(x * 0.7f)), RelativeLayout.LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
        viewHolder.sent_voice_container.setLayoutParams(params);

        convertView.setTag(viewHolder);
    }else{
        viewHolder = (ViewHolder) convertView.getTag();
    }


            viewHolder.sent_voice_play.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                // Check if the Item is currently playing 
                    if(list.get(i).isPlaying()){
                        // Stop audio and save  progress
                        stopItemAudio(list.get(i), viewHolder.sent_voice_seekbar);
                        // Change button's image to Pause ||
                        ((ImageView) v).setImageDrawable(((MainActivity) context).pauseDrawable);

                    }else{
                        // Start audio and update the SeekBar
                        playItemAudio(list.get(i), viewHolder.sent_voice_seekbar);
                        // Change button's image to Play >
                        ((ImageView) v).setImageDrawable(((MainActivity) context).playDrawable);
                    }
                }
            });

            if(list.get(i).isPlaying()){
                viewHolder.sent_voice_seekbar.setActive(list.get(i).seekbar_resume_position);
                viewHolder.sent_voice_play.setImageDrawable(((MainActivity) context).pauseDrawable);
            }else{
                viewHolder.sent_voice_seekbar.setInactive(list.get(i).seekbar_resume_position);
                viewHolder.sent_voice_play.setImageDrawable(((MainActivity) context).playDrawable);
            }


    return convertView;
}




public void stopItemAudio(AudioRow item, SelfUpdatingSeekBar seekBar){

    if(mMediaPlayer == null){
        mMediaPlayer = new MediaPlayer();
    }

    mMediaPlayer.stop();
    item.setPlaying(false);
    int percentage = getProgressPercentage(mMediaPlayer.getCurrentPosition(), mMediaPlayer.getDuration());

    item.setSeekBarResumePosition(percentage);
    item.setMediaPlayerResumePosition(mMediaPlayer.getCurrentPosition());
    seekBar.setInactive(percentage);
    notifyDataSetChanged();

}
public void playItemAudio(final AudioRow item, SelfUpdatingSeekBar seekBar){
    if(mMediaPlayer == null){
        mMediaPlayer = new MediaPlayer();
    }

    try {
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(item.audioPath);
        mMediaPlayer.prepare();
        mMediaPlayer.seekTo(item.mediaPlayer_resume_position);
        seekBar.setActive(item.seekbar_resume_position); // Starts from where it stopped
        mMediaPlayer.start();

        // declaring Item started to play
        item.setPlaying(true);
        notifyDataSetChanged();

        mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                item.setSeekBarResumePosition(0);
                item.setPlaying(false);
            }
        });
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalStateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }



public int getProgressPercentage(long currentDuration, long totalDuration){
    Double percentage = (double) 0;

    long currentSeconds = (int) (currentDuration / 1000);
    long totalSeconds = (int) (totalDuration / 1000);

    // calculating percentage
    percentage =(((double)currentSeconds)/totalSeconds)*100;

    // return percentage
    return percentage.intValue();
}

}

The problem is when I click on Item1 , and it starts playing, and click on Item2 both SeekBars animate at the same time, and that is expected since I did not stop Item1 on the click of Item2

My question is:

  • What is the proper way to stop the previous played Item1 on the click of Item2 ?

Your SeekBar implementation should not have its own MediaPlayer . Have only one MediaPlayer instance in your activity. Every time a "play" button is clicked in your list view you do something like this:

MediaPlayer mMediaPlayer = ((MainActivity) context).getSingleMediaPlayer();
((MainActivity) context).setCurrentlyPlayingItem(i);
mMediaPlayer.reset();
mMediaPlayer.setDataSource(item.audioPath);
mMediaPlayer.prepare();
notifyDatasetHasChanged()

Now on your getView() implementation, if the first argument: i is not equal to the currently playing item of MainActivity its SeekBar should be at zero.

MainActivity should also be the one implementing the update runnable. At this point you might want to use just a normal SeekBar . To update the correct View, you need to figure out first if it is visible, because the user could have scrolled to a different position, so you need this function in your MainActivity :

public View getVisibleItemView(int position){
        if(listview.getCount() > 0) {
            int start = listview.getFirstVisiblePosition();
            for (int i = start, j = listview.getLastVisiblePosition(); i <= j; i++) {
                if (i == position) {
                    return listview.getChildAt(i - start);
                }
            }
        }
        return null;
    }

This function returns the view with the SeekBar that you want to update, but only if it is visible, otherwise it returns null. in your update runnable you should call it, and if the result is not null, you update the SeekBar .

Also, you might want to consider using an interface instead of casting context references to MainActivity in your adapter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM