【问题标题】:How can i set progress of correct SeekBar from getView?如何从 getView 设置正确 SeekBar 的进度?
【发布时间】:2026-01-03 23:25:02
【问题描述】:

我有一个带有自定义列表项的 ListView,每个列表项中有一个按钮和一个 SeekBar。现在,当单击按钮时,音频开始播放,我想为单击按钮的位置设置 SeekBar 的进度。使用我拥有的代码,正确的 SeekBar 开始移动,但是当我上下滚动几次时,另一个 SeekBar 开始移动。我已经尝试了很多东西,这是我的最后一个:

import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Handler;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

public class MyListAdapter extends ArrayAdapter<Audio> {

ArrayList<Audio> audios;
Context context;
int resource;

private static MediaPlayer mediaPlayer;

public Handler mHandler = new Handler();
TextView totalTime;
TextView playingTime;

private MyListAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull ArrayList<Audio> audios) {
    super(context, resource, audios);
    Collections.reverse(audios);
    this.audios = audios;
    this. context = context;
    this.resource = resource;
}

@NonNull
 @Override
 public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    final ViewHolder viewHolder;
    View row;
    if (convertView == null) {
        LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        row = layoutInflater.inflate(R.layout.audio_cell, parent, false);

        viewHolder = new ViewHolder();
        viewHolder.playButton = (ImageButton) row.findViewById(R.id.playButton);
        viewHolder.seekBar = (SeekBar) row.findViewById(R.id.seekBar);
        viewHolder.playButton.setTag(position);
        viewHolder.seekBar.setTag(position);
        row.setTag(viewHolder);
    } else {
        row = convertView;
        viewHolder = (ViewHolder) row.getTag();
    }

    viewHolder.seekBar.setProgress(0);
    //I get the audio url here
    final Audio audio = getItem(position);
    assert audio != null;
    String theURL = audio.getURL();

    //Button Click
    viewHolder.playButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //here I call the method to play the audio and after that I want 
            //to update the SeekBar
            playAudio(theURL);

            updateSeekBar(viewHolder.seekBar);
        }
    });
    return row;
}

private void updateSeekBar(final SeekBar sb) {

    Runnable mUpdateTimeTask = new Runnable() {
        @Override
        public void run() {
            if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                int mCurrentPosition = mediaPlayer.getCurrentPosition() / 1000;
                mHandler.postDelayed(this, 100);
                sb.setProgress(mCurrentPosition);
            }
        }
    };
    mHandler.postDelayed(mUpdateTimeTask, 100);
}
}

这是我的 ViewHolder 类:

public class ViewHolder {
    public ImageButton playButton;
    public SeekBar seekBar;
}

现在正如我所说,我尝试了很多东西,但似乎没有任何效果,我想我尝试过的东西都是一样的,因为我每次都得到相同的结果。只要我上下滚动几次,它就会在不同的 SeekBar 上设置进度。任何帮助表示赞赏!

【问题讨论】:

  • 为什么不在适配器中使用开始按钮?
  • 适配器中有音频路径吗?
  • 将搜索栏添加到音频开始
  • @EnamulHaque 是的音频路径在适配器中。我用更多细节更新了答案。而且我不明白“将搜索栏添加到音频开始”,这是什么意思?
  • 好的。会给适配器吗?请检查

标签: java android listview seekbar listitem


【解决方案1】:

列表视图循环使用 getView() 中的视图,因为您正在使用 getTag() 和 setTAg()。因此,当单击项目时,它会反映在其他一些位置,并且歌曲开始在其他位置播放,因为它正在重用查看。

解决这个问题。创建一个全局 int 值来存储被点击项目的位置。

private int mSelectedItem = -1;

每当点击播放按钮时,将该位置添加到 mSelectedItem

viewHolder.playButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        //Store the position here
         mSelectedItem=position;
            //here I call the method to play the audio and after that I want 
            //to update the SeekBar
            playAudio(theURL);

            updateSeekBar(viewHolder.seekBar);
        }
    });

然后像这样更新 seekBar

调用方法时传递位置

updateSeekBar(viewHolder.seekBar,position);

检查位置是否与您单击的位置相同并更新搜索栏

private void updateSeekBar(final SeekBar sb,int position) {
    final Runnable mUpdateTimeTask=null;
    if(mSelectedItem==position){
         mUpdateTimeTask = new Runnable() {
           @Override
           public void run() {
               if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                    int mCurrentPosition = mediaPlayer.getCurrentPosition() / 1000;
                    mHandler.postDelayed(this, 100);
                    sb.setProgress(mCurrentPosition);
                }
            }
        };
        mHandler.postDelayed(mUpdateTimeTask, 100);
    }else{
         mHandler.removeCallbacks(mUpdateTimeTask);
         sb.setProgress(0);
    }
}    

【讨论】:

  • 一开始也可以,但是当我向下滚动时,它也会更新一个不同的 seekBar。
  • 我无法从 mUpdateTimeTask 中删除回调,因为它在 if 语句中声明。但是我不需要在 onClick() 方法之外调用 updateSeekBar() 吗?这样每次滚动时都会调用它?
  • 您需要在 if 语句之外添加 Runnable 引用。是的,您应该在 onclick 之外调用它,这样每次滚动时都会调用它。请参阅更新后的代码
  • mUpdateTimeTask 可能尚未在 else 语句中初始化。但我在 getView meyhod 中尝试过 if (mSelectedItem==position) { updateSeekbar(viewHolder.seekBar, postiotion;} ,我认为这样做也是如此。但仍然不起作用。
【解决方案2】:

你可以像下面这样写..

  1. 声明变量

        private MediaPlayer mediaPlayer;
        private double startTime = 0;
        private double finalTime = 0;
        public  int oneTimeOnly = 0;
    
  2. 更改 viewHolder.playButton

      viewHolder.playButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //Being...
    
                    try {
                        mediaPlayer = new MediaPlayer();
                        mediaPlayer.setDataSource(theURL);
                        try {
    
    
                            // Log.e("ddd***",tv_directory.getText().toString());
    
                            if(mediaPlayer.isPlaying()){
                                Log.e("isplaying**",mediaPlayer.isPlaying()+"");
    
                                mediaPlayer.pause();
                                finalTime = mediaPlayer.getDuration();
                                startTime = mediaPlayer.getCurrentPosition();
    
                                if (oneTimeOnly == 0) {
                                    seekBar.setMax((int) finalTime);
                                    oneTimeOnly = 1;
                                }
    
    
                            }else{
                                // Log.e("isplaying**",mediaPlayer.isPlaying()+"");
    
    
    
                                mediaPlayer.prepare();
                                //mediaPlayer.start();
    
    
                                mediaPlayer.start();
    
    
                                finalTime = mediaPlayer.getDuration();
                                startTime = mediaPlayer.getCurrentPosition();
    
                                if (oneTimeOnly == 0) {
                                    seekBar.setMax((int) finalTime);
                                    oneTimeOnly = 1;
                                }
    
    
    
    
    
                            }
                        } catch (Exception e) {
                            Log.e("err***",e.toString());
                        }
    
    
    
                        myHandler.postDelayed(new Runnable(){
                            public void run() {
                                startTime = mediaPlayer.getCurrentPosition();
                                tv_start_time.setText(String.format("%d min, %d sec",
                                        TimeUnit.MILLISECONDS.toMinutes((long) startTime),
                                        TimeUnit.MILLISECONDS.toSeconds((long) startTime) -
                                                TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.
                                                        toMinutes((long) startTime)))
    
                                );
    
    
    
                                seekBar.setProgress((int) startTime);
    
                                myHandler.postDelayed(this, 100);
                            }
                        }, 100);
    
    
                    } catch (Exception e) {
                        Log.e("err***",e.toString());
    
                    }
                    //End....
                }
            });
    

【讨论】:

  • 好了,对于 seekBar,我必须说 'viewHolder.seekBar' 吗?因为它无法解析符号'seekBar'。
  • 现在我导入了 seekBar,但我不能 setMax 或 setProgress 到它。
  • 添加完整的适配器
  • 我添加了完整的适配器类。 (如果这就是你的意思)
【解决方案3】:

好的,我找到了问题,因为

if(convertView == null)

仅适用于屏幕上出现的第一个项目。 Here 它解释了为什么会发生这种情况。在我的情况下,this 答案有效。这不是最好的方法,但对于具有少量项目的 ListView 来说,它可以工作。

【讨论】: