简体   繁体   English

ListView 中与 MediaPlayer 关联的多个 SeekBar

[英]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 .我有一个显示语音消息的ListView (就像 WhatsApp 的),我使用的是BaseAdapter ,我的布局包含一个播放button和一个SeekBar What I want is to play the audio on the click of the play button , and update the SeekBar .我想要的是点击播放button播放音频,并更新SeekBar

So I implemented this custom SeekBar which updates itself.所以我实现了这个自定义的SeekBar ,它会自我更新。

SelfUpdatingSeekBar.java : 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:这是我的getView()样子:

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问题是当我点击Item1 ,它开始播放,并同时点击Item2两个 SeekBars 动画,这是预期的,因为我没有在点击Item2停止Item1

My question is:我的问题是:

  • What is the proper way to stop the previous played Item1 on the click of Item2 ?单击Item2停止先前播放的Item1的正确方法是什么?

Your SeekBar implementation should not have its own MediaPlayer .您的SeekBar实现不应有自己的MediaPlayer Have only one MediaPlayer instance in your activity.在您的活动中只有一个MediaPlayer实例。 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.现在在您的getView()实现上,如果第一个参数: i不等于MainActivity当前播放的项目,则它的SeekBar应该为零。

MainActivity should also be the one implementing the update runnable. MainActivity也应该是实现更新 runnable 的那个。 At this point you might want to use just a normal SeekBar .此时您可能只想使用普通的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 :要更新正确的视图,您需要首先确定它是否可见,因为用户可能已经滚动到不同的位置,因此您需要在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.此函数返回带有您要更新的SeekBar的视图,但SeekBar是它是可见的,否则返回 null。 in your update runnable you should call it, and if the result is not null, you update the SeekBar .在您的更新 runnable 中,您应该调用它,如果结果不为空,则更新SeekBar

Also, you might want to consider using an interface instead of casting context references to MainActivity in your adapter.此外,您可能需要考虑使用接口而不是将上下文引用转换为适配器中的MainActivity

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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