[英]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:我的问题是:
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.