SeekBar 和 ListView 回收问题
SeekBar and ListView recycling issue
我在我的新应用程序中使用这个项目,它显示带有播放按钮和 SeekBar 进度的曲目列表。我可以播放曲目,但更新 SeekBar 时遇到问题。
我使用 Runnable 和 Handler 来更新 SeekBar。
当我点击播放时,child 视图的 SeekBar 正确移动,但其回收位置的 child 也得到了更新。例如,我有一个包含 10 个项目的列表,其中只有 5 个可见项目。当第 1 首曲目的 SeekBar 得到更新时,第 6 首曲目也被更新。如果我有更大的列表,第 11 或第 16 位也会发生同样的事情。
关于如何正确更新它的任何建议?
这是适配器
public class ListViewAdapter extends BaseAdapter {
private Activity activity;
private List<Ring> list;
private LayoutInflater inflater;
private int mediaFileLengthInMilliseconds;
private final Handler handler = new Handler();
private MediaPlayer player;
private AssetManager assetManager;
public ListViewAdapter(Activity activity, List<Ring> list) {
this.activity = activity;
this.list = list;
this.inflater = LayoutInflater.from(this.activity);
assetManager = activity.getAssets();
player = new MediaPlayer();
}
@Override
public int getCount() {
return list.size();
}
@Override
public Ring getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = this.inflater.inflate(R.layout.list_view_item, parent, false);
holder.play = (ImageButton) convertView.findViewById(R.id.play);
holder.fav = (Button) convertView.findViewById(R.id.fav);
holder.set = (Button) convertView.findViewById(R.id.setAs);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.seekBarProgress = (SeekBar) convertView.findViewById(R.id.bar);
holder.seekBarProgress.setMax(99);
holder.seekBarProgress.setEnabled(false);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(list.get(position).getName());
holder.title.setTypeface(Typeface.createFromAsset(activity.getAssets(), "fonts/OpenSans-Light.ttf"));
holder.play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (player.isPlaying()) {
player.pause();
holder.play.setImageDrawable(ResourcesCompat.getDrawable(activity.getResources(), android.R.drawable.ic_media_play, null));
holder.primarySeekBarProgressUpdater();
} else {
player.reset();
AssetFileDescriptor afd = null;
try {
afd = assetManager.openFd(list.get(position).getName() + ".mp3");
player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
player.prepare();
player.start();
mediaFileLengthInMilliseconds = player.getDuration();
player.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
holder.seekBarProgress.setSecondaryProgress(percent);
}
});
holder.seekBarProgress.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.bar) {
if (player.isPlaying()) {
SeekBar sb = (SeekBar) v;
int playPositionInMillisecconds = (mediaFileLengthInMilliseconds / 100) * sb.getProgress();
player.seekTo(playPositionInMillisecconds);
}
}
return true;
}
});
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
holder.play.setImageDrawable(ResourcesCompat.getDrawable(activity.getResources(), android.R.drawable.ic_media_play, null));
}
});
holder.primarySeekBarProgressUpdater();
holder.play.setImageDrawable(ResourcesCompat.getDrawable(activity.getResources(), android.R.drawable.ic_media_pause, null));
}catch (IOException e) {
e.printStackTrace();
}
}
}
});
return convertView;
}
private class ViewHolder {
TextView title;
ImageButton play;
Button set;
Button fav;
SeekBar seekBarProgress;
void primarySeekBarProgressUpdater() {
seekBarProgress.setProgress((int) (((float) player.getCurrentPosition() / mediaFileLengthInMilliseconds) * 100)); // This math construction give a percentage of "was playing"/"song length"
if (player.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
primarySeekBarProgressUpdater();
}
};
handler.postDelayed(notification,100);
}
}
}
}
正如您已经指出的,列表视图项目是回收的。因此,您的 Handler
应该使用 SeekBar
的值更新您的 支持数据模型 并在适配器上调用 notifyDataSetChanged()
。 SeekBar
值的实际设置应由适配器的 getView()
.
处理
这不是一个完美的修复,因为您的代码存在很多问题,但它应该可以解决您更新多个列表项的问题。
- 将
position
变量添加到您的 支持列表模型。
- 将您要更新的项目的位置传递到
primarySeekBarProgressUpdater
- 致电
notifyDataSetChanged()
- 在您的
getView
中,根据您的 支持数据模型 设置搜索栏位置
void primarySeekBarProgressUpdater(final int i) {
list.get(i).setPosition((int) (((float) player.getCurrentPosition() / mediaFileLengthInMilliseconds) * 100)); // This math construction give a percentage of "was playing"/"song length"
notifyDataSetChanged();
if (player.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
primarySeekBarProgressUpdater(i);
}
};
handler.postDelayed(notification,100);
}
}
public View getView(final int position, View convertView, ViewGroup parent) {
...
holder.seekBarProgress.setProgress(list.get(position).getPosition());
...
}
我在我的新应用程序中使用这个项目,它显示带有播放按钮和 SeekBar 进度的曲目列表。我可以播放曲目,但更新 SeekBar 时遇到问题。
我使用 Runnable 和 Handler 来更新 SeekBar。 当我点击播放时,child 视图的 SeekBar 正确移动,但其回收位置的 child 也得到了更新。例如,我有一个包含 10 个项目的列表,其中只有 5 个可见项目。当第 1 首曲目的 SeekBar 得到更新时,第 6 首曲目也被更新。如果我有更大的列表,第 11 或第 16 位也会发生同样的事情。 关于如何正确更新它的任何建议?
这是适配器
public class ListViewAdapter extends BaseAdapter {
private Activity activity;
private List<Ring> list;
private LayoutInflater inflater;
private int mediaFileLengthInMilliseconds;
private final Handler handler = new Handler();
private MediaPlayer player;
private AssetManager assetManager;
public ListViewAdapter(Activity activity, List<Ring> list) {
this.activity = activity;
this.list = list;
this.inflater = LayoutInflater.from(this.activity);
assetManager = activity.getAssets();
player = new MediaPlayer();
}
@Override
public int getCount() {
return list.size();
}
@Override
public Ring getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = this.inflater.inflate(R.layout.list_view_item, parent, false);
holder.play = (ImageButton) convertView.findViewById(R.id.play);
holder.fav = (Button) convertView.findViewById(R.id.fav);
holder.set = (Button) convertView.findViewById(R.id.setAs);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.seekBarProgress = (SeekBar) convertView.findViewById(R.id.bar);
holder.seekBarProgress.setMax(99);
holder.seekBarProgress.setEnabled(false);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(list.get(position).getName());
holder.title.setTypeface(Typeface.createFromAsset(activity.getAssets(), "fonts/OpenSans-Light.ttf"));
holder.play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (player.isPlaying()) {
player.pause();
holder.play.setImageDrawable(ResourcesCompat.getDrawable(activity.getResources(), android.R.drawable.ic_media_play, null));
holder.primarySeekBarProgressUpdater();
} else {
player.reset();
AssetFileDescriptor afd = null;
try {
afd = assetManager.openFd(list.get(position).getName() + ".mp3");
player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
player.prepare();
player.start();
mediaFileLengthInMilliseconds = player.getDuration();
player.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
holder.seekBarProgress.setSecondaryProgress(percent);
}
});
holder.seekBarProgress.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.bar) {
if (player.isPlaying()) {
SeekBar sb = (SeekBar) v;
int playPositionInMillisecconds = (mediaFileLengthInMilliseconds / 100) * sb.getProgress();
player.seekTo(playPositionInMillisecconds);
}
}
return true;
}
});
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
holder.play.setImageDrawable(ResourcesCompat.getDrawable(activity.getResources(), android.R.drawable.ic_media_play, null));
}
});
holder.primarySeekBarProgressUpdater();
holder.play.setImageDrawable(ResourcesCompat.getDrawable(activity.getResources(), android.R.drawable.ic_media_pause, null));
}catch (IOException e) {
e.printStackTrace();
}
}
}
});
return convertView;
}
private class ViewHolder {
TextView title;
ImageButton play;
Button set;
Button fav;
SeekBar seekBarProgress;
void primarySeekBarProgressUpdater() {
seekBarProgress.setProgress((int) (((float) player.getCurrentPosition() / mediaFileLengthInMilliseconds) * 100)); // This math construction give a percentage of "was playing"/"song length"
if (player.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
primarySeekBarProgressUpdater();
}
};
handler.postDelayed(notification,100);
}
}
}
}
正如您已经指出的,列表视图项目是回收的。因此,您的 Handler
应该使用 SeekBar
的值更新您的 支持数据模型 并在适配器上调用 notifyDataSetChanged()
。 SeekBar
值的实际设置应由适配器的 getView()
.
这不是一个完美的修复,因为您的代码存在很多问题,但它应该可以解决您更新多个列表项的问题。
- 将
position
变量添加到您的 支持列表模型。 - 将您要更新的项目的位置传递到
primarySeekBarProgressUpdater
- 致电
notifyDataSetChanged()
- 在您的
getView
中,根据您的 支持数据模型 设置搜索栏位置
void primarySeekBarProgressUpdater(final int i) {
list.get(i).setPosition((int) (((float) player.getCurrentPosition() / mediaFileLengthInMilliseconds) * 100)); // This math construction give a percentage of "was playing"/"song length"
notifyDataSetChanged();
if (player.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
primarySeekBarProgressUpdater(i);
}
};
handler.postDelayed(notification,100);
}
}
public View getView(final int position, View convertView, ViewGroup parent) {
...
holder.seekBarProgress.setProgress(list.get(position).getPosition());
...
}