android - SeekBar 和 ListView 回收问题

标签 android listview seekbar

我在我的新应用程序中使用这个项目,它显示带有播放按钮和 SeekBar 进度的轨道列表。我能够播放轨道,但更新 SeekBar 时遇到问题。

我使用 Runnable 和 Handler 来更新 SeekBar。 当我点击播放时,该 subview 的 SeekBar 正确移动,但处于回收位置的 subview 也得到了更新。例如,我有一个包含 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);
            }
        }
    }
}

最佳答案

正如您已经指出的, ListView 项目被回收。因此,您的 Handler 应该使用 SeekBar 的值更新您的支持数据模型,并调用 notifyDataSetChanged() 在适配器上。 SeekBar 值的实际设置应由适配器的 getView() 处理。

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

  1. 位置变量添加到您的支持列表模型
  2. 将您要更新的项目的位置传递到 primarySeekBarProgressUpdater
  3. 调用notifyDataSetChanged()
  4. 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());
    ...
}

关于android - SeekBar 和 ListView 回收问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31396281/

相关文章:

Android 编辑文本光标不出现

java - 在预览上叠加透明颜色

java - Android - ListView EditText 焦点问题

android 自定义 Listview 项目困惑

android - 如何在动画中实现带有淡入淡出的圆形图像?

java - 如何确定哪个 android 小部件正在调用我的函数?

android - 无法在 ScrollView 中拖动搜索栏

java - 无法使用 getApplicationInfo 从名称获取子包

android - 如何计算android中自定义相机中的剩余照片数

java - 屏幕关闭时继续播放音乐