给定部分完成的MusicPlayer项目,实现其中未完成的service部分:
1、创建MusicService类,通过service组件实现后台播放音乐的功能;
2、在MainActivity中通过ServiceConnection连接MusicService,实现对音乐播放的控制;
3、使用Handler机制在MainActivity和MusicService之间进行通信。
目前已有代码:
相关的资源文件,可自行寻找
activity_main.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/music_bg"
android:gravity="center"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="160dp"
tools:ignore="UselessParent">
<RelativeLayout
android:id="@+id/rl_title"
android:layout_width="300dp"
android:layout_height="70dp"
android:layout_centerHorizontal="true"
android:background="@drawable/title_bg"
android:gravity="center_horizontal"
android:paddingStart="80dp"
tools:ignore="RtlSymmetry">
<TextView
android:id="@+id/tv_music_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/song_name"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="@android:color/black"/>
<TextView
android:layout_marginTop="4dp"
android:id="@+id/tv_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_music_title"
android:layout_alignStart="@id/tv_music_title"
android:text="@string/pop_music"
android:textSize="10sp"
tools:ignore="SmallSp" />
<SeekBar
android:id="@+id/sb"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_below="@id/rl_time"
android:layout_alignParentBottom="true"
android:thumb="@null" />
<RelativeLayout
android:layout_marginTop="4dp"
android:id="@+id/rl_time"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_below="@id/tv_type">
<TextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/zero_time"
android:textSize="10sp"
tools:ignore="SmallSp" />
<TextView
android:id="@+id/tv_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="@string/zero_time"
android:textSize="10sp"
tools:ignore="RelativeOverlap,SmallSp" />
</RelativeLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="340dp"
android:layout_height="90dp"
android:layout_below="@id/rl_title"
android:layout_centerHorizontal="true"
android:background="@drawable/btn_bg"
android:gravity="center_vertical"
android:paddingStart="120dp"
android:paddingEnd="10dp">
<Button
android:id="@+id/btn_play"
android:layout_width="0dp"
android:layout_height="25dp"
android:layout_margin="4dp"
android:layout_weight="1"
android:background="@drawable/btn_bg_selector"
android:text="@string/play"
android:textSize="10sp"
tools:ignore="ButtonStyle,SmallSp" />
<Button
android:id="@+id/btn_pause"
android:layout_width="0dp"
android:layout_height="25dp"
android:layout_margin="4dp"
android:layout_weight="1"
android:background="@drawable/btn_bg_selector"
android:text="@string/pause"
android:textSize="10sp"
tools:ignore="ButtonStyle,SmallSp" />
<Button
android:id="@+id/btn_continue_play"
android:layout_width="0dp"
android:layout_height="25dp"
android:layout_margin="4dp"
android:layout_weight="1"
android:background="@drawable/btn_bg_selector"
android:text="@string/cont"
android:textSize="10sp"
tools:ignore="ButtonStyle,SmallSp" />
<Button
android:id="@+id/btn_exit"
android:layout_width="0dp"
android:layout_height="25dp"
android:layout_margin="4dp"
android:layout_weight="1"
android:background="@drawable/btn_bg_selector"
android:text="@string/exit"
android:textSize="10sp"
tools:ignore="ButtonStyle,SmallSp" />
</LinearLayout>
<ImageView
android:id="@+id/iv_music"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerVertical="true"
android:layout_marginStart="35dp"
android:layout_marginBottom="50dp"
android:src="@drawable/img_music"
android:contentDescription="@string/iv" />
</RelativeLayout>
</LinearLayout>
MainActivity.java
java
package cn.itcast.musicplayer;
import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static SeekBar sb;
private static TextView tv_progress, tv_total;
private ObjectAnimator animator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
tv_progress = findViewById(R.id.tv_progress);
tv_total = findViewById(R.id.tv_total);
sb = findViewById(R.id.sb);
findViewById(R.id.btn_play).setOnClickListener(this);
findViewById(R.id.btn_pause).setOnClickListener(this);
findViewById(R.id.btn_continue_play).setOnClickListener(this);
findViewById(R.id.btn_exit).setOnClickListener(this);
//为滑动条添加事件监听
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean
fromUser) { //滑动条进度改变时,会调用此方法
if (progress == seekBar.getMax()) { //当滑动条滑到末端时,结束动画
animator.pause(); //停止播放动画
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {//滑动条开始滑动时调用
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) { //滑动条停止滑动时调用
//根据拖动的进度改变音乐播放进度
int progress = seekBar.getProgress();//获取seekBar的进度
}
});
ImageView iv_music = findViewById(R.id.iv_music);
animator = ObjectAnimator.ofFloat(iv_music, "rotation", 0f, 360.0f);
animator.setDuration(10000); //动画旋转一周的时间为10秒
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(-1); //-1表示设置动画无限循环
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_play: //播放按钮点击事件
animator.start(); //播放动画
break;
case R.id.btn_pause: //暂停按钮点击事件
animator.pause(); //暂停播放动画
break;
case R.id.btn_continue_play: //继续播放按钮点击事件
animator.start(); //播放动画
break;
case R.id.btn_exit: //退出按钮点击事件
finish(); //关闭音乐播放界面
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//解绑服务
}
}
当前已经有一个用户界面,其中包括了播放、暂停、继续播放和退出按钮,以及一个旋转动画效果。现在,我们需要将MusicService与MainActivity连接起来,以实现音乐的播放和控制功能。
步骤1:创建MusicService类
单击鼠标右键并选择【New】-->【Service】-->【Service】
步骤 1: 创建 MusicService 类
首先,你需要创建一个名为 MusicService
的类,该类将负责处理音乐播放和与 MainActivity 之间的通信。
这里你需要准备一个mp3格式的文件
java
package cn.itcast.musicplayer;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
public class MusicService extends Service {
private MediaPlayer mediaPlayer;
public MusicService() {
}
@Override
public IBinder onBind(Intent intent) {
return new MusicBinder();
}
public class MusicBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
// 在这里设置音乐资源,例如 mediaPlayer.setDataSource(your_music_uri);
mediaPlayer = MediaPlayer.create(this, R.raw.music); // 加载音乐资源
}
// 添加播放音乐的方法
public void playMusic() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
// 添加暂停音乐的方法
public void pauseMusic() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
// 添加继续播放音乐的方法
public void continueMusic() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
@Override
public void onDestroy() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
super.onDestroy();
}
}
相关变量描述:
-
MusicService
是一个 Android 服务类,用于处理音乐播放相关的功能。 -
mediaPlayer
是用于播放音乐的 MediaPlayer 对象,它负责加载音乐资源、播放、暂停和继续播放音乐。 -
MusicBinder
内部类继承自 Binder,用于绑定服务与其他组件之间的通信。 -
onBind
方法用于返回MusicBinder
对象,以便其他组件可以与服务进行绑定。 -
onCreate
方法在服务创建时被调用,它初始化了mediaPlayer
并加载音乐资源。在这个示例中,音乐资源是从R.raw.music
中加载的。 -
playMusic
方法用于播放音乐,如果音乐未在播放状态,则调用mediaPlayer.start()
来开始播放。 -
pauseMusic
方法用于暂停音乐,如果音乐正在播放,则调用mediaPlayer.pause()
来暂停。 -
continueMusic
方法用于继续播放音乐,如果音乐已暂停,则调用mediaPlayer.start()
来继续播放。 -
onDestroy
方法在服务销毁时被调用,它释放了mediaPlayer
对象的资源,确保不会产生内存泄漏。
服务允许其他组件与其绑定,以控制音乐的播放、暂停和继续播放;载了一个音乐资源(在这个示例中是 R.raw.music
),并使用 MediaPlayer
对象进行音乐播放
在AndroidManifest.xml中注册MusicService(检查)
确保在AndroidManifest.xml文件中注册MusicService,以便应用能够正常启动该服务。
一般在我们创建service文件后,会自动进行注册的
xml<service android:name=".MusicService" />
步骤 2: 在 MainActivity 中连接 MusicService
在 MainActivity
中,添加代码来连接 MusicService
并控制音乐的播放、暂停和继续。
java
private MusicService musicService;
private boolean isBound = false;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicService = binder.getService();
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MusicService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (isBound) {
unbindService(serviceConnection);
isBound = false;
}
}
步骤 3: 在 MainActivity 中调用 MusicService 的方法
在 onClick
方法中调用 MusicService
的方法来控制音乐的播放、暂停和继续。
java
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_play: // 播放按钮点击事件
if (isBound) {
musicService.playMusic();
}
animator.start(); // 播放动画
break;
case R.id.btn_pause: // 暂停按钮点击事件
if (isBound) {
musicService.pauseMusic();
}
animator.pause(); // 暂停播放动画
break;
case R.id.btn_continue_play: // 继续播放按钮点击事件
if (isBound) {
musicService.continueMusic();
}
animator.start(); // 播放动画
break;
case R.id.btn_exit: // 退出按钮点击事件
finish(); // 关闭音乐播放界面
break;
}
}
当前,我们就已经初步完成了简单音乐播放器的播放、暂停、继续、退出功能;
你可以尝试此时运行项目测试效果!
步骤 4: 修改MusicService(以实现通信更新UI)
添加获取相关信息函数
java
// 获取音乐总时长
public int getTotalDuration() {
return mediaPlayer.getDuration();
}
// 获取音乐当前播放进度
public int getCurrentPosition() {
return mediaPlayer.getCurrentPosition();
}
// 设置音乐播放进度
public void seekTo(int position) {
mediaPlayer.seekTo(position);
}
// 更新UI,发送消息给MainActivity
private void updateUI(int progress, int totalDuration) {
if (handler != null) {
Message message = Message.obtain(handler, UPDATE_UI, progress, totalDuration);
handler.sendMessage(message);
}
}
@Override
public void onCreate() {
super.onCreate();
mediaPlayer = MediaPlayer.create(this, R.raw.music); // 加载音乐资源
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// 音乐播放完成时的处理
}
});
// 定时发送消息以更新UI
Runnable runnable = new Runnable() {
@Override
public void run() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
int progress = mediaPlayer.getCurrentPosition();
int totalDuration = mediaPlayer.getDuration();
updateUI(progress, totalDuration);
}
handler.postDelayed(this, DELAY_MILLIS);
}
};
handler.postDelayed(runnable, DELAY_MILLIS);
}
这段代码是为 MusicService
添加了一些重要的功能,以实现与 MainActivity
之间的通信并更新UI。以下是代码的描述:
-
getTotalDuration
函数用于获取音乐的总时长。它通过mediaPlayer.getDuration()
方法获取音乐的总时长,然后返回该值。 -
getCurrentPosition
函数用于获取音乐的当前播放进度。通过mediaPlayer.getCurrentPosition()
方法获取音乐的当前播放进度,然后返回该值。 -
seekTo
函数用于设置音乐的播放进度。接受一个整数参数position
,表示要设置的音乐播放进度,并使用mediaPlayer.seekTo(position)
方法来实现进度的跳转。 -
updateUI
函数用于发送消息给MainActivity
,以便更新UI元素。它接受两个参数,分别是当前播放进度progress
和音乐总时长totalDuration
。它创建一个Message
对象,并通过handler.sendMessage(message)
发送消息给MainActivity
,以便更新UI元素,比如进度条和文本。 -
在
onCreate
方法中,定时发送消息以更新UI。通过一个Runnable
定时任务,在其中获取当前播放进度和音乐总时长,然后调用updateUI
函数发送消息给MainActivity
,以实现不断更新UI元素的目的。
这些函数和逻辑使 MusicService
能够与 MainActivity
进行通信,传递音乐播放进度和总时长,以便 MainActivity
能够更新UI元素,提供用户友好的音乐播放体验。
步骤 5: 修改MainActivity(以实现通信更新UI)
在MusicService中获取音乐总时长,并在MainActivity中更新tv_total和进度条的位置,以及格式化音乐的总时长和进度。
java
private static final int UPDATE_UI = 1;
public final static Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == UPDATE_UI) {
int progress = msg.arg1;
int totalDuration = msg.arg2;
updateUI(progress, totalDuration);
}
}
};
public static void updateUI(int progress, int totalDuration) {
sb.setProgress(progress);
tv_progress.setText(formatDuration(progress));
// 更新左侧显示的总时间
tv_total.setText(formatDuration(totalDuration));
}
// 辅助方法来更新进度
private void updateProgress(int progress) {
tv_progress.setText(formatDuration(progress));
}
// 辅助方法来格式化音乐时长
private static String formatDuration(int duration) {
int minutes = (duration / 1000) / 60;
int seconds = (duration / 1000) % 60;
return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
}
// 添加方法来更新总时长
private void updateTotalDuration(int duration) {
tv_total.setText(formatDuration(duration));
sb.setMax(duration);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicService = binder.getService();
isBound = true;
// 获取音乐总时长并更新UI
int totalDuration = musicService.getTotalDuration();
updateTotalDuration(totalDuration);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
private void init() {
// 初始化控件和按钮点击事件监听
tv_progress = findViewById(R.id.tv_progress);
tv_total = findViewById(R.id.tv_total);
sb = findViewById(R.id.sb);
// ...
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress == seekBar.getMax()) {
animator.pause();
}
updateProgress(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 更新音乐播放进度
int progress = seekBar.getProgress();
musicService.seekTo(progress); // 添加 seekTo 方法用于定位音乐进度
}
});
// ...
这段代码是为 MainActivity
添加了与 MusicService
之间的通信,以便实现音乐播放进度的动态更新和音乐总时长的显示。以下是代码的描述:
-
在
MainActivity
中定义了一个handler
,这是一个静态的Handler
对象,它用于处理从MusicService
发送的消息,以便更新UI元素。通过UPDATE_UI
常量来标识消息类型。 -
updateUI
函数是用于更新UI元素的核心方法。接受两个参数,分别是当前播放进度progress
和音乐总时长totalDuration
。在这个方法中,进度条的位置会被设置为当前播放进度,左侧的文本tv_progress
会被更新为格式化后的播放进度,而左侧的总时长文本tv_total
会被更新为格式化后的音乐总时长。 -
updateProgress
方法是一个辅助方法,用于更新播放进度。接受一个参数progress
,并更新左侧的文本tv_progress
为格式化后的播放进度。 -
formatDuration
方法是一个辅助方法,用于格式化音乐时长。接受一个整数duration
,表示音乐的时长(以毫秒为单位),然后将其格式化为分:秒的形式。 -
updateTotalDuration
方法用于更新总时长。它接受一个参数duration
,表示音乐的总时长,并更新左侧的总时长文本tv_total
为格式化后的音乐总时长,并设置进度条的最大值为音乐的总时长。 -
在
serviceConnection
中,当MusicService
与MainActivity
连接成功后,会获取音乐的总时长并调用updateTotalDuration
方法来更新UI元素。 -
在
sb
(SeekBar)的事件监听器中,通过onProgressChanged
方法,当进度条的进度发生变化时,会调用updateProgress
方法来更新左侧的播放进度文本。在onStopTrackingTouch
方法中,当用户拖动进度条时,会调用musicService.seekTo(progress)
方法来定位音乐的进度。
这些代码改动使 MainActivity
能够与 MusicService
协同工作,以实现音乐播放进度的动态更新和音乐总时长的显示。这对于提供用户友好的音乐播放体验至关重要。
步骤 6: 增加音乐结束后的处理细节
对于这些新的问题,我们可以进行以下修改和处理:
- 停止动画: 随着音乐播放的完成,动画应该随之停止。我们在
MusicService
中添加了一个音乐播放完成回调,以便在音乐结束时暂停动画。这样,用户可以看到音乐已经完成,同时动画不再旋转,提供了明确的视觉指示。如下所示:
java
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// 音乐播放完成时的处理
animator.pause(); // 停止动画
}
});
- 左侧动的文字内容无法到达最大值: 为了确保在音乐播放完成后左侧的时间文本达到最大值,我们在音乐播放完成回调中更新了左侧的时间文本。通过调用
tv_progress.setText(formatDuration(getTotalDuration()))
,我们将左侧的时间文本设置为音乐的总时长,以表明音乐已完成
java
@Override
public void onCreate() {
//......
@Override
public void onCompletion(MediaPlayer mp) {
// 音乐播放完成时的处理
animator.pause(); // 停止动画
tv_progress.setText(formatDuration(getTotalDuration()));
}
});
}
- 在音乐播放完成后没有提醒: 我们添加了一种通知用户音乐已完成的方式。在
MusicService
中的音乐播放完成回调中,我们使用showToast
函数显示一个短暂的提示消息。这种提醒可以根据你的需求进行扩展,例如,你可以选择显示通知、执行其他操作或添加更多的用户反馈。
java
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
public void onCreate() {
//......
@Override
public void onCompletion(MediaPlayer mp) {
// 音乐播放完成时的处理
animator.pause(); // 停止动画
showToast("音乐已完成"); // 显示音乐播放完成的提示
}
});
}
完整代码
MainActivity.java
java
package cn.itcast.musicplayer;
import static cn.itcast.musicplayer.MusicService.mediaPlayer;
import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Locale;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static SeekBar sb;
public static TextView tv_progress, tv_total;
public static ObjectAnimator animator;
private MusicService musicService;
private boolean isBound = false;
private static final int UPDATE_UI = 1;
// 辅助方法来格式化音乐时长
public static String formatDuration(int duration) {
int minutes = (duration / 1000) / 60;
int seconds = (duration / 1000) % 60;
return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
}
// 添加方法来更新总时长
private void updateTotalDuration(int duration) {
tv_total.setText(formatDuration(duration));
sb.setMax(duration);
}
// 辅助方法来更新进度
private void updateProgress(int progress) {
tv_progress.setText(formatDuration(progress));
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicService = binder.getService();
isBound = true;
// 获取音乐总时长并更新UI
int totalDuration = musicService.getTotalDuration();
updateTotalDuration(totalDuration);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
public final static Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == UPDATE_UI) {
int progress = msg.arg1;
int totalDuration = msg.arg2;
updateUI(progress, totalDuration);
}
}
};
public static void updateUI(int progress, int totalDuration) {
sb.setProgress(progress);
tv_progress.setText(formatDuration(progress));
tv_total.setText(formatDuration(totalDuration));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
// 初始化控件和按钮点击事件监听
tv_progress = findViewById(R.id.tv_progress);
tv_total = findViewById(R.id.tv_total);
sb = findViewById(R.id.sb);
findViewById(R.id.btn_play).setOnClickListener(this);
findViewById(R.id.btn_pause).setOnClickListener(this);
findViewById(R.id.btn_continue_play).setOnClickListener(this);
findViewById(R.id.btn_exit).setOnClickListener(this);
// ...
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress == seekBar.getMax()) {
animator.pause();
}
updateProgress(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 更新音乐播放进度
int progress = seekBar.getProgress();
musicService.seekTo(progress); // 添加 seekTo 方法用于定位音乐进度
}
});
// 初始化动画
ImageView iv_music = findViewById(R.id.iv_music);
animator = ObjectAnimator.ofFloat(iv_music, "rotation", 0f, 360.0f);
animator.setDuration(10000); //动画旋转一周的时间为10秒
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(-1); //-1表示设置动画无限循环
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_play:
if (isBound) {
musicService.playMusic();
}
animator.start();
break;
case R.id.btn_pause:
if (isBound) {
musicService.pauseMusic();
}
animator.pause();
break;
case R.id.btn_continue_play:
if (isBound) {
musicService.continueMusic();
}
animator.start();
break;
case R.id.btn_exit:
finish();
break;
}
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MusicService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
//解绑服务
if (isBound) {
unbindService(serviceConnection);
isBound = false;
}
}
}
MusicService.java
java
package cn.itcast.musicplayer;
import static cn.itcast.musicplayer.MainActivity.formatDuration;
import static cn.itcast.musicplayer.MainActivity.handler;
import static cn.itcast.musicplayer.MainActivity.animator;
import static cn.itcast.musicplayer.MainActivity.tv_progress;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.Toast;
public class MusicService extends Service {
public static MediaPlayer mediaPlayer;
private final IBinder binder = new MusicBinder();
private final int UPDATE_UI = 1;
private final int DELAY_MILLIS = 1000; // 延迟1秒发送消息
public MusicService() {
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public class MusicBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
public void onCreate() {
super.onCreate();
mediaPlayer = MediaPlayer.create(this, R.raw.music); // 加载音乐资源
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// 音乐播放完成时的处理
animator.pause(); // 停止动画
tv_progress.setText(formatDuration(getTotalDuration()));
showToast("音乐已完成"); // 显示音乐播放完成的提示
}
});
// 定时发送消息以更新UI
Runnable runnable = new Runnable() {
@Override
public void run() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
int progress = mediaPlayer.getCurrentPosition();
int totalDuration = mediaPlayer.getDuration();
updateUI(progress, totalDuration);
}
handler.postDelayed(this, DELAY_MILLIS);
}
};
handler.postDelayed(runnable, DELAY_MILLIS);
}
// 更新UI,发送消息给MainActivity
private void updateUI(int progress, int totalDuration) {
if (handler != null) {
Message message = Message.obtain(handler, UPDATE_UI, progress, totalDuration);
handler.sendMessage(message);
}
}
// 添加播放音乐的方法
public void playMusic() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
// 添加暂停音乐的方法
public void pauseMusic() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
// 添加继续播放音乐的方法
public void continueMusic() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
@Override
public void onDestroy() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
super.onDestroy();
}
// 获取音乐总时长
public int getTotalDuration() {
return mediaPlayer.getDuration();
}
// 获取音乐当前播放进度
public int getCurrentPosition() {
return mediaPlayer.getCurrentPosition();
}
// 设置音乐播放进度
public void seekTo(int position) {
mediaPlayer.seekTo(position);
}
}
实现效果
最重要的是能在
后台播放音乐