Хорошо, я новичок ie Android и java. Делая это только для хобби и изучая его в свободное время, которое я могу найти.
Я наткнулся на SeekBar и решил создать простой медиаплеер, чтобы поэкспериментировать с ним. После начала работы я открыл Spotify и попытался имитировать c его поведение SeekBar.
Итак, вот концепция: У меня один Runnable работает каждые 200 мс, когда MediaPlayer находится в статус воспроизведения, чтобы обновить левую метку времени и позицию SeekBar.
Теперь, когда вы держите Spotify SeekBar и перетаскиваете его, происходит следующее: что крайняя левая метка времени обновляется при перемещении SeekBar, отображая временную метку того, где находится SeekBar, без влияния на musi c. Это только эффективно изменит «позицию» musi c, когда мы выпустим SeekBar.
Итак, мой подход к этому подходу был следующим: создать второй Runnable, который будет вызываться через Handler, когда пользователь удерживает SeekBar и освободить обработчик, когда пользователь выпустит SeekBar. Кроме того, этот исполняемый файл будет обновляться каждые 50 мс, потому что перетаскивание панели является довольно быстрым и визуально изменяет метку таймера, когда пользователь перемещает панель.
Теперь вот код моей MainActivity, выполняющий все это:
package com.heymilkshake.simpleaudioplayer;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button playBtn;
private Button stopBtn;
private Button skipForward10Btn;
private Button skipBackward10Btn;
private TextView displayStatus;
private TextView songCurrentTime;
private TextView songTimeEnd;
private SeekBar seekBar;
private MediaPlayer mediaPlayer;
private Handler seekBarHandler;
private Runnable updateSeekBar;
private Runnable updateTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViews();
tagButtons();
createPlayer();
initialize();
}
private void initialize() {
songCurrentTime.setText(formatTime(mediaPlayer.getCurrentPosition()));
songTimeEnd.setText(formatTime(mediaPlayer.getDuration()));
seekBar.setMax(mediaPlayer.getDuration());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (mediaPlayer.isPlaying()) {
seekBarHandler.removeCallbacks(updateSeekBar);
}
seekBarHandler.postDelayed(updateTimer, 0);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
seekBarHandler.removeCallbacks(updateTimer);
mediaPlayer.seekTo(seekBar.getProgress());
if (mediaPlayer.isPlaying()) {
seekBarHandler.postDelayed(updateSeekBar, 0);
}
}
});
seekBarHandler = new Handler();
updateSeekBar = new Runnable() {
@Override
public void run() {
updateSeekAndTime();
seekBarHandler.postDelayed(this, 200);
}
};
updateTimer = new Runnable() {
@Override
public void run() {
songCurrentTime.setText(formatTime(seekBar.getProgress()));
seekBarHandler.postDelayed(this, 50);
}
};
}
private String formatTime(int ms) {
return String.format("%d:%02d",
TimeUnit.MILLISECONDS.toMinutes((long) ms),
TimeUnit.MILLISECONDS.toSeconds((long) ms) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes((long) ms)));
}
private void createPlayer() {
mediaPlayer = MediaPlayer.create(this, R.raw.come_alive);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
handleStopButton();
}
});
}
private void findViews() {
playBtn = findViewById(R.id.button_play);
stopBtn = findViewById(R.id.button_stop);
skipForward10Btn = findViewById(R.id.button_skip_forward_10);
skipBackward10Btn = findViewById(R.id.button_skip_backward_10);
displayStatus = findViewById(R.id.display_status);
songCurrentTime = findViewById(R.id.song_time_start);
songTimeEnd = findViewById(R.id.song_time_end);
seekBar = findViewById(R.id.seek_bar);
}
private void tagButtons() {
int i = 0;
for (Button button : Arrays.asList(
playBtn,
stopBtn,
skipForward10Btn,
skipBackward10Btn)) {
button.setTag(i++);
button.setOnClickListener(this);
}
}
@Override
public void onClick(View v) {
int clickedTag = (int) v.getTag();
switch (clickedTag) {
case 0:
handlePlayButton();
break;
case 1:
handleStopButton();
break;
case 2:
handleForward10Btn();
break;
case 3:
handleBackwards10Btn();
break;
default:
}
}
private void handleBackwards10Btn() {
if (mediaPlayer.getCurrentPosition() - 10000 < 0) {
mediaPlayer.seekTo(0);
} else {
mediaPlayer.seekTo(mediaPlayer.getCurrentPosition() - 10000);
}
updateSeekAndTime();
}
private void handleForward10Btn() {
if (mediaPlayer.getCurrentPosition() + 10000 > mediaPlayer.getDuration()) {
mediaPlayer.seekTo(mediaPlayer.getDuration());
} else {
mediaPlayer.seekTo(mediaPlayer.getCurrentPosition() + 10000);
}
updateSeekAndTime();
}
private void updateSeekAndTime() {
seekBar.setProgress(mediaPlayer.getCurrentPosition());
songCurrentTime.setText(formatTime(seekBar.getProgress()));
}
private void handleStopButton() {
mediaPlayer.pause();
mediaPlayer.seekTo(0);
seekBarHandler.removeCallbacks(updateSeekBar);
updateSeekAndTime();
displayStatus.setText("Stopped");
playBtn.setText("Play ►");
}
private void handlePlayButton() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
seekBarHandler.removeCallbacks(updateSeekBar);
displayStatus.setText("Paused");
playBtn.setText("Play ►");
} else {
mediaPlayer.start();
seekBarHandler.postDelayed(updateSeekBar, 0);
displayStatus.setText("Playing");
playBtn.setText("Pause ||");
}
}
}
Когда я тестировал свой код, у меня было такое поведение: полоса, перепрыгивающая назад и вперед к месту, где играет музыка c, пока пользователь перетаскивает ее (gif для визуализации нежелательного поведения)
Ну, я прекрасно понимаю, почему это происходит: updateSeekBar
выполняется каждые 200 мс, вызывая метод updateSeekAndTime()
, который меняет метку и полосу на текущую позицию, где играет музыка c. Итак, когда onStartTrackingTouch
обнаруживает, что пользователь удерживает SeekBar, мы запускаем updateTimer
с возможностью запуска каждые 50 мс. Этот Runnable изменяет текст метки времени одновременно с updateSeekBar
Runnable, вызывая нежелательное поведение, описанное выше.
Чтобы решить эту проблему, я изменил код анонимной реализации интерфейса SeekBar.OnSeekBarChangeListener()
, добавив некоторые проверки условий : Если песня воспроизводится, и пользователь берет SeekBar, удалите обратный вызов на updateSeekBar
, чтобы он прекратил работу, и когда пользователь отпустит панель, запустите ее снова. Итак, вот изменения кода:
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (mediaPlayer.isPlaying()) {
seekBarHandler.removeCallbacks(updateSeekBar);
}
seekBarHandler.postDelayed(updateTimer, 0);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
seekBarHandler.removeCallbacks(updateTimer);
mediaPlayer.seekTo(seekBar.getProgress());
if (mediaPlayer.isPlaying()) {
seekBarHandler.postDelayed(updateSeekBar, 0);
}
}
});
Хорошо, это сработало просто замечательно! Посмотрите на другой GIF:
Но это заставило меня задуматься, и именно здесь я спрашиваю вас, ребята:
- Есть ли лучший способ сделать это?
- Есть ли какой-либо способ для потока, который зацикливается с помощью обработчика, чтобы дать команду другому потоку "ждать", пока он выполняется?
- Как бы вы решили эту проблему ??
ВАЖНОЕ ПРИМЕЧАНИЕ: Попробуйте упростить или поговорить о вещах простым способом, поскольку я всего лишь Java и Android новичок.
Спасибо всем !!