【问题标题】:Android - SeekBar and MediaPlayerAndroid - SeekBar 和 MediaPlayer
【发布时间】:2017-04-28 08:28:31
【问题描述】:

我需要在我的应用程序中将我的SeekBar 与我的MediaPlayer 连接起来。

我通过 xml 设置了 SeekBar,如下所示:

<SeekBar
        android:id="@+id/song_seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"/>

并按照这个SO answer 来实现它。

这是我的代码:

public class Song_main extends AppCompatActivity {
private final int SONG_REQUEST_CODE = 1;

private Uri song;
private TextView selectSong;
private SeekBar seekBar;
private Handler handler;
private MediaPlayer mediaPlayer;


private boolean repeatPressedTwice = false;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.app_bar_song_main);

    Toolbar toolbar = (Toolbar) findViewById(R.id.song_main_toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayShowTitleEnabled(false);

    seekBar = (SeekBar) findViewById(R.id.song_seekbar);
    handler = new Handler();

    notSelected();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.song, menu);

    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == R.id.song_plus) {
        Intent selectIntent = new Intent(Intent.ACTION_GET_CONTENT);
        selectIntent.setType("audio/*");
        startActivityForResult(selectIntent, SONG_REQUEST_CODE);
    }

    return true;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SONG_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        if ((data != null) && (data.getData()!=null)) {
            song = data.getData();
            setup();
        }
    }
}

private void notSelected() {
    selectSong = (TextView) findViewById(R.id.select_song_textview);
    selectSong.setText(getResources().getString(R.string.song_not_selected));
}

public void onPlayButtonClicked(View v) {
    ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
    if (!mediaPlayer.isPlaying()) {
        mediaPlayer.start();
        pb.setImageResource(R.drawable.pause);

        updateSeekBar();

    } else {
        mediaPlayer.pause();
        pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
    }
}

public void onControlsClicked(View v) {
    if (v.getId() == R.id.fast_forward) {
        int pos = mediaPlayer.getCurrentPosition();
        pos += 1500;
        mediaPlayer.seekTo(pos);
    }
    else if (v.getId() == R.id.fast_backward) {
        int pos = mediaPlayer.getCurrentPosition();
        pos -= 1500;
        mediaPlayer.seekTo(pos);
    }
    else if (v.getId() == R.id.skip_backward) {
        mediaPlayer.seekTo(0);
    }
}

public void onRepeatClicked(View v) {
    if (!repeatPressedTwice) {
        // TODO: change visual color of repeat button
        mediaPlayer.setLooping(true);
        Toast.makeText(this, "repeat enabled", Toast.LENGTH_SHORT).show();
        repeatPressedTwice = true;
    } else {
        mediaPlayer.setLooping(false);
    }

}

private void setup() {
    /* the song has been select setup the interface */


    /* displays song name in title */
    TextView titleView = (TextView) findViewById(R.id.song_appbar_title);
    String songName;

    ContentResolver contentResolver = this.getContentResolver();
    Cursor cursor = contentResolver.query(song, null, null, null, null);

    if (cursor != null && cursor.moveToFirst()) {
        songName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
        titleView.setText(songName);
    }


    /* removes the notSelected String */
    selectSong.setVisibility(View.GONE);

    /* setup media player */
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    try {
        mediaPlayer.setDataSource(getApplicationContext(), song);
        mediaPlayer.prepareAsync();
    } catch (IOException e) {
        Toast.makeText(this, "file not found", Toast.LENGTH_SHORT).show();
    }

    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
            /* show media player layout */
            RelativeLayout mpl = (RelativeLayout) findViewById(R.id.media_player_layout);
            mpl.setVisibility(View.VISIBLE);

            mediaPlayer.start();
            ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
            pb.setImageResource(R.drawable.pause);

        }
    });

    mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
            pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
        }
    });



    seekBar = (SeekBar) findViewById(R.id.song_seekbar);
    seekBar.setMax(mediaPlayer.getDuration()); 

    updateSeekBar();


}

private void updateSeekBar() {
    seekBar.setProgress(mediaPlayer.getCurrentPosition()/1000);
    handler.postDelayed(runnable, 1000);
}

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        updateSeekBar();
    }
};


@Override
public void onStop() {
    super.onStop();
    if (mediaPlayer!=null)
        mediaPlayer.stop();
}
}

流程从onOptionsItemSelected方法开始。

seekBar 行为正确,每秒递增。现在的问题是它在歌曲结束之前就结束了。

我尝试添加

seekBar.setMax(mediaPlayer.getDuration());

setup 方法中,但这会导致条形图根本不动。

【问题讨论】:

    标签: java android xml media-player seekbar


    【解决方案1】:

    您需要定义单独的Runnable 并在MediaPlayer 启动后每 x 毫秒(取决于您)触发一次。

    定义一个函数updateSeekbarlike,

    private void updateSeekBar() {
        audioSeek.setProgress(player.getCurrentPosition());
        txtCurrentTime.setText(milliSecondsToTimer(player.getCurrentPosition()));
        seekHandler.postDelayed(runnable, 50);
    }
    

    还有Runnable

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            updateSeekBar();
        }
    };
    

    现在您只需在游戏开始时拨打updateSeekbar 一次。在你的情况下:

    public void onPlayButtonClicked(View v) {
        ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
        if (!mediaPlayer.isPlaying()) {
            mediaPlayer.start();
            pb.setImageResource(R.drawable.pause);
    
            updateSeekBar();
    
        } else {
            mediaPlayer.pause();
            pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
        }
    }
    

    仅供参考

    函数milliSecondsToTimer工作如下

    private String milliSecondsToTimer(long milliseconds) {
        String finalTimerString = "";
        String secondsString = "";
    
        // Convert total duration into time
        int hours = (int) (milliseconds / (1000 * 60 * 60));
        int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
        int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
        // Add hours if there
        if (hours > 0) {
            finalTimerString = hours + ":";
        }
    
        // Prepending 0 to seconds if it is one digit
        if (seconds < 10) {
            secondsString = "0" + seconds;
        } else {
            secondsString = "" + seconds;
        }
    
        finalTimerString = finalTimerString + minutes + ":" + secondsString;
    
        // return timer string
        return finalTimerString;
    }
    

    更新

    你打错地方了setMax。更新setup()函数如下

    private void setup() {
        /* the song has been select setup the interface */
        /* displays song name in title */
        TextView titleView = (TextView) findViewById(R.id.song_appbar_title);
        String songName;
    
        ContentResolver contentResolver = this.getContentResolver();
        Cursor cursor = contentResolver.query(song, null, null, null, null);
    
        if (cursor != null && cursor.moveToFirst()) {
            songName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            titleView.setText(songName);
        }
    
    
        /* removes the notSelected String */
        selectSong.setVisibility(View.GONE);
    
        /* setup media player */
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        try {
            mediaPlayer.setDataSource(getApplicationContext(), song);
            mediaPlayer.prepareAsync();
        } catch (IOException e) {
            Toast.makeText(this, "file not found", Toast.LENGTH_SHORT).show();
        }
    
        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
            /* show media player layout */
                seekBar.setMax(mediaPlayer.getDuration());
    
                RelativeLayout mpl = (RelativeLayout) findViewById(R.id.media_player_layout);
                mpl.setVisibility(View.VISIBLE);
                mediaPlayer.start();
    
                updateSeekBar();
    
    
                ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
                pb.setImageResource(R.drawable.pause);
    
            }
        });
    
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
                pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
            }
        });
    
    }
    

    【讨论】:

    • 感谢您的回答。它有点工作,但它不跟踪歌曲的长度,因此搜索栏进度在歌曲实际完成之前完成。我怎么能解决这个问题?我尝试添加seekBar.setMax(mediaPlayer.getDuration()),但之后搜索栏似乎没有移动
    • 您必须在mediaPlayer.setDataSource(...) 之后的setup 函数中将数据源提供给MediaPlayer 时设置它
    • 你确定吗? mediaPlayer 还没有准备好,因为mediaPlayer.prepareAsync() 甚至还没有启动,在这种情况下seekBar.setMax(mediaPlayer.getDuration()) 不会被设置为空吗?我不应该把它放在 'setOnPreparedListener()' 里面吗? ...正如我所怀疑的...它导致应用程序崩溃
    • 哦,对不起,这是我的错误。你应该在玩家准备好之后调用它
    • 是的,它会导致 seekBar 根本不动。请锁定已编辑的问题。我已经输入了您的代码,并解释了仍然无法正常工作的地方。
    【解决方案2】:

    您已实现 OnSeekBarChangeListener 并在 onCreate() 添加以下行:-

    seekBar = (SeekBar) findViewById(R.id.seekBar);
    

    并覆盖onProgressChanged() 方法,在此方法中,您可以使用以下行在搜索栏中设置进度:

    mPlayer.seekTo(progress); 
    seekBar.setProgress(progress);
    

    在初始化 MediaPlayer 并按下播放按钮后,您应该创建一个处理程序并发布可运行文件,以便您可以使用 MediaPlayer 的当前位置更新 SeekBar(在 UI 线程本身中),如下所示:

    private Handler mHandler = new Handler();
    //Make sure you update Seekbar on UI thread
    MainActivity.this.runOnUiThread(new Runnable(){
        @Override
        public void run() {
            if(mMediaPlayer != null){
                int mCurrentPosition = mMediaPlayer.getCurrentPosition() / 1000;
                mSeekBar.setProgress(mCurrentPosition);
            }
        mHandler.postDelayed(this, 1000);
        }
    });
    

    并每秒更新一次该值。

    如果您需要在用户拖动您的 SeekBar 时更新 MediaPlayer 的位置,您应该将 OnSeekBarChangeListener 添加到您的 SeekBar 并在那里执行:

    mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
         @Override
         public void onStopTrackingTouch(SeekBar seekBar) {
    
         }
         @Override
         public void onStartTrackingTouch(SeekBar seekBar) {
    
         }
         @Override
         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
             if(mMediaPlayer != null && fromUser){
                 mMediaPlayer.seekTo(progress * 1000);
         }
      }
    });
    

    【讨论】:

      【解决方案3】:

      您需要在播放歌曲时更新搜索栏

      public void updateProgressBar() {
              runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                      mHandler.postDelayed(mUpdateTimeTask, 100);
                  }
              });
      
          }  
      

      下面的 Runnable 方法来更新 seekbar

      private Runnable mUpdateTimeTask = new Runnable() {
              public void run() {
                  if (MusicService.isRunning()) {
                      duration = MusicService.getDur();
                      long currSongPosition = MusicService.getPosn();
                      totTime.setText(Utility.milliSecondsToTimer(duration));
                      fromTime.setText(Utility.milliSecondsToTimer(currSongPosition));
                      int progress = Utility.getProgressPercentage(currSongPosition, duration);
                  songProgressBar.setProgress(progress);
                  updateProgressBar();
              }
          }
      };
      

      使用以下函数,您可以从歌曲当前位置和歌曲持续时间获取进度百分比

       public static int getProgressPercentage(long currentDuration, long totalDuration) {
              Double percentage;
              long currentSeconds = (int) (currentDuration / 1000);
              long totalSeconds = (int) (totalDuration / 1000);
              percentage = (((double) currentSeconds) / totalSeconds) * 100;
              return percentage.intValue();
          }
      

      【讨论】:

      • 感谢您的回答。你能解释一下代码的作用吗?甚至供其他用户进一步参考
      • @Daniele 请检查并告诉我
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多