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().

处理

这不是一个完美的修复,因为您的代码存在很多问题,但它应该可以解决您更新多个列表项的问题。

  1. position 变量添加到您的 支持列表模型
  2. 将您要更新的项目的位置传递到 primarySeekBarProgressUpdater
  3. 致电notifyDataSetChanged()
  4. 在您的 getView 中,根据您的 支持数据模型
  5. 设置搜索栏位置

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());
    ...
}