|
|
|
|
@ -1,6 +1,5 @@
|
|
|
|
|
package com.example.musicplayer.view;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import android.animation.ObjectAnimator;
|
|
|
|
|
import android.animation.ValueAnimator;
|
|
|
|
|
import android.app.ActivityOptions;
|
|
|
|
|
@ -53,218 +52,287 @@ import org.litepal.LitePal;
|
|
|
|
|
import butterknife.BindView;
|
|
|
|
|
import de.hdodenhof.circleimageview.CircleImageView;
|
|
|
|
|
|
|
|
|
|
// MainActivity类继承自BaseActivity,是音乐播放器应用的主界面类,负责处理界面展示、用户交互以及与音乐播放相关服务的交互等功能
|
|
|
|
|
public class MainActivity extends BaseActivity {
|
|
|
|
|
private static final String TAG = "MainActivity";
|
|
|
|
|
|
|
|
|
|
// 通过ButterKnife框架绑定SeekBar控件,用于显示音乐播放进度
|
|
|
|
|
@BindView(R.id.sb_progress)
|
|
|
|
|
SeekBar mSeekBar;
|
|
|
|
|
// 通过ButterKnife框架绑定TextView控件,用于显示歌曲名称
|
|
|
|
|
@BindView(R.id.tv_song_name)
|
|
|
|
|
TextView mSongNameTv;
|
|
|
|
|
// 通过ButterKnife框架绑定TextView控件,用于显示歌手名称
|
|
|
|
|
@BindView(R.id.tv_singer)
|
|
|
|
|
TextView mSingerTv;
|
|
|
|
|
// 通过ButterKnife框架绑定RippleView控件,点击可切换到下一首歌曲
|
|
|
|
|
@BindView(R.id.song_next)
|
|
|
|
|
RippleView mNextIv;
|
|
|
|
|
// 通过ButterKnife框架绑定Button控件,用于控制音乐的播放/暂停
|
|
|
|
|
@BindView(R.id.btn_player)
|
|
|
|
|
Button mPlayerBtn;
|
|
|
|
|
// 通过ButterKnife框架绑定CircleImageView控件,用于显示歌曲封面图片
|
|
|
|
|
@BindView(R.id.circle_img)
|
|
|
|
|
CircleImageView mCoverIv;
|
|
|
|
|
// 通过ButterKnife框架绑定LinearLayout控件,可能是包含播放相关控件的布局容器
|
|
|
|
|
@BindView(R.id.linear_player)
|
|
|
|
|
LinearLayout mLinear;
|
|
|
|
|
|
|
|
|
|
private boolean isChange; //拖动进度条
|
|
|
|
|
private boolean isSeek;//标记是否在暂停的时候拖动进度条
|
|
|
|
|
private boolean flag; //用做暂停的标记
|
|
|
|
|
private int time; //记录暂停的时间
|
|
|
|
|
|
|
|
|
|
private boolean isExistService;//服务是否存活
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ObjectAnimator mCircleAnimator;//动画
|
|
|
|
|
// 标记是否正在拖动进度条,用于防止进度条更新冲突
|
|
|
|
|
private boolean isChange;
|
|
|
|
|
// 标记是否在暂停的时候拖动进度条,用于后续恢复播放时正确设置播放位置
|
|
|
|
|
private boolean isSeek;
|
|
|
|
|
// 用作暂停的标记,用于区分不同的播放/暂停操作场景
|
|
|
|
|
private boolean flag;
|
|
|
|
|
// 记录暂停的时间,单位可能是毫秒,用于恢复播放时定位播放位置
|
|
|
|
|
private int time;
|
|
|
|
|
|
|
|
|
|
// 标记音乐播放相关服务是否存活,用于判断一些操作是否可行
|
|
|
|
|
private boolean isExistService;
|
|
|
|
|
|
|
|
|
|
// 属性动画对象,用于实现歌曲封面图片的旋转动画效果
|
|
|
|
|
private ObjectAnimator mCircleAnimator;
|
|
|
|
|
// 当前播放的歌曲对象,包含歌曲的相关信息如名称、歌手、时长等
|
|
|
|
|
private Song mSong;
|
|
|
|
|
// MediaPlayer对象,用于实际的音频播放控制,不过这里看起来主要通过服务中的MediaPlayer来操作
|
|
|
|
|
private MediaPlayer mMediaPlayer;
|
|
|
|
|
// 线程对象,用于在后台更新SeekBar的进度显示
|
|
|
|
|
private Thread mSeekBarThread;
|
|
|
|
|
// 用于获取PlayerService中的播放状态相关信息和操作方法的Binder对象
|
|
|
|
|
private PlayerService.PlayStatusBinder mPlayStatusBinder;
|
|
|
|
|
// 用于获取DownloadService中的下载相关操作方法的Binder对象(这里未看到详细使用)
|
|
|
|
|
private DownloadService.DownloadBinder mDownloadBinder;
|
|
|
|
|
|
|
|
|
|
// 服务连接对象,用于与PlayerService建立连接并获取服务端的Binder对象
|
|
|
|
|
private ServiceConnection connection = new ServiceConnection() {
|
|
|
|
|
// 当与服务成功连接时调用,获取服务端传递过来的Binder对象
|
|
|
|
|
@Override
|
|
|
|
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
|
|
|
mPlayStatusBinder = (PlayerService.PlayStatusBinder) service;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当与服务意外断开连接时调用(比如服务崩溃),这里暂时没做具体处理
|
|
|
|
|
@Override
|
|
|
|
|
public void onServiceDisconnected(ComponentName name) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//绑定下载服务
|
|
|
|
|
// 下载服务的服务连接对象,用于与DownloadService建立连接并获取服务端的Binder对象
|
|
|
|
|
private ServiceConnection mDownloadConnection = new ServiceConnection() {
|
|
|
|
|
// 当与下载服务成功连接时调用,获取服务端传递过来的Binder对象
|
|
|
|
|
@Override
|
|
|
|
|
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
|
|
|
|
mDownloadBinder = (DownloadService.DownloadBinder) iBinder;
|
|
|
|
|
if(isExistService) seekBarStart();
|
|
|
|
|
// 如果服务存在,启动SeekBar进度更新
|
|
|
|
|
if (isExistService) seekBarStart();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当与下载服务意外断开连接时调用(比如服务崩溃),这里暂时没做具体处理
|
|
|
|
|
@Override
|
|
|
|
|
public void onServiceDisconnected(ComponentName componentName) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 重写onDestroy方法,在Activity销毁时执行清理和保存相关操作
|
|
|
|
|
@Override
|
|
|
|
|
public void onDestroy() {
|
|
|
|
|
super.onDestroy();
|
|
|
|
|
//解绑
|
|
|
|
|
// 解除与PlayerService的绑定
|
|
|
|
|
unbindService(connection);
|
|
|
|
|
// 解除与DownloadService的绑定
|
|
|
|
|
unbindService(mDownloadConnection);
|
|
|
|
|
|
|
|
|
|
//将播放的服务提升至前台服务
|
|
|
|
|
// 创建一个启动PlayerService的Intent,目的是将播放的服务提升至前台服务(针对Android 8.0及以上系统的处理)
|
|
|
|
|
Intent playIntent = new Intent(MainActivity.this, PlayerService.class);
|
|
|
|
|
//Android 8.0以上
|
|
|
|
|
// Android 8.0以上,以前台服务的方式启动服务
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
|
startForegroundService(playIntent);
|
|
|
|
|
} else {
|
|
|
|
|
// Android 8.0以下,普通启动服务
|
|
|
|
|
startService(playIntent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 取消在EventBus上的注册,避免内存泄漏等问题
|
|
|
|
|
EventBus.getDefault().unregister(this);
|
|
|
|
|
if (mSeekBarThread != null || mSeekBarThread.isAlive()) mSeekBarThread.interrupt();
|
|
|
|
|
// 如果SeekBar线程存在且正在运行,中断该线程
|
|
|
|
|
if (mSeekBarThread!= null || mSeekBarThread.isAlive()) mSeekBarThread.interrupt();
|
|
|
|
|
// 获取当前播放的歌曲对象
|
|
|
|
|
Song song = FileUtil.getSong();
|
|
|
|
|
// 将歌曲当前播放时间设置为从服务获取到的当前时间,保存歌曲信息
|
|
|
|
|
song.setCurrentTime(mPlayStatusBinder.getCurrentTime());
|
|
|
|
|
FileUtil.saveSong(song);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取Activity对应的布局资源ID,这里返回activity_main布局的ID
|
|
|
|
|
@Override
|
|
|
|
|
protected int getLayoutId() {
|
|
|
|
|
return R.layout.activity_main;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化视图相关操作,在Activity创建时调用
|
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
|
|
|
@Override
|
|
|
|
|
protected void initView() {
|
|
|
|
|
// 在EventBus上注册,以便接收事件通知
|
|
|
|
|
EventBus.getDefault().register(this);
|
|
|
|
|
// 初始化LitePal数据库(可能用于数据持久化等操作,比如保存歌曲相关信息)
|
|
|
|
|
LitePal.getDatabase();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//设置属性动画
|
|
|
|
|
// 创建属性动画,设置CircleImageView(歌曲封面图片)的旋转动画,从0度旋转到360度
|
|
|
|
|
mCircleAnimator = ObjectAnimator.ofFloat(mCoverIv, "rotation", 0.0f, 360.0f);
|
|
|
|
|
// 设置动画持续时间为30秒
|
|
|
|
|
mCircleAnimator.setDuration(30000);
|
|
|
|
|
// 设置线性插值器,使动画匀速进行
|
|
|
|
|
mCircleAnimator.setInterpolator(new LinearInterpolator());
|
|
|
|
|
// 设置动画重复次数为无限次
|
|
|
|
|
mCircleAnimator.setRepeatCount(-1);
|
|
|
|
|
// 设置动画重复模式为重新开始
|
|
|
|
|
mCircleAnimator.setRepeatMode(ValueAnimator.RESTART);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前播放的歌曲对象
|
|
|
|
|
mSong = FileUtil.getSong();
|
|
|
|
|
if (mSong.getSongName() != null) {
|
|
|
|
|
// 如果歌曲名称不为空,说明有正在播放的歌曲,进行相关界面设置
|
|
|
|
|
if (mSong.getSongName()!= null) {
|
|
|
|
|
Log.d(TAG, "initView: " + mSong.toString());
|
|
|
|
|
// 显示包含播放相关控件的布局容器
|
|
|
|
|
mLinear.setVisibility(View.VISIBLE);
|
|
|
|
|
// 设置TextView显示歌曲名称
|
|
|
|
|
mSongNameTv.setText(mSong.getSongName());
|
|
|
|
|
// 设置TextView显示歌手名称
|
|
|
|
|
mSingerTv.setText(mSong.getSinger());
|
|
|
|
|
// 设置SeekBar的最大进度为歌曲的总时长(单位可能需要转换,这里看起来是以秒为单位存储时长)
|
|
|
|
|
mSeekBar.setMax((int) mSong.getDuration());
|
|
|
|
|
// 设置SeekBar的当前进度为歌曲的当前播放时间
|
|
|
|
|
mSeekBar.setProgress((int) mSong.getCurrentTime());
|
|
|
|
|
// 如果歌曲封面图片的URL为空,通过CommonUtil工具类根据歌手名称设置默认图片
|
|
|
|
|
if (mSong.getImgUrl() == null) {
|
|
|
|
|
CommonUtil.setSingerImg(MainActivity.this, mSong.getSinger(), mCoverIv);
|
|
|
|
|
} else {
|
|
|
|
|
// 使用Glide库加载歌曲封面图片,设置占位图和加载失败时显示的图片
|
|
|
|
|
Glide.with(this)
|
|
|
|
|
.load(mSong.getImgUrl())
|
|
|
|
|
.apply(RequestOptions.placeholderOf(R.drawable.welcome))
|
|
|
|
|
.apply(RequestOptions.errorOf(R.drawable.welcome))
|
|
|
|
|
.into(mCoverIv);
|
|
|
|
|
.load(mSong.getImgUrl())
|
|
|
|
|
.apply(RequestOptions.placeholderOf(R.drawable.welcome))
|
|
|
|
|
.apply(RequestOptions.errorOf(R.drawable.welcome))
|
|
|
|
|
.into(mCoverIv);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 如果没有正在播放的歌曲,设置默认的显示文本和图片
|
|
|
|
|
mSongNameTv.setText(getString(R.string.app_name));
|
|
|
|
|
mSingerTv.setText(getString(R.string.welcome_start));
|
|
|
|
|
mCoverIv.setImageResource(R.drawable.jay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//如果播放服务还存活
|
|
|
|
|
if(ServiceUtil.isServiceRunning(this,PlayerService.class.getName())){
|
|
|
|
|
// 如果PlayerService正在运行(服务存活)
|
|
|
|
|
if (ServiceUtil.isServiceRunning(this, PlayerService.class.getName())) {
|
|
|
|
|
// 设置播放按钮为选中状态(表示正在播放)
|
|
|
|
|
mPlayerBtn.setSelected(true);
|
|
|
|
|
// 启动歌曲封面图片的旋转动画
|
|
|
|
|
mCircleAnimator.start();
|
|
|
|
|
// 标记服务存活
|
|
|
|
|
isExistService = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//处理服务
|
|
|
|
|
// 初始化相关服务(启动并绑定服务)
|
|
|
|
|
initService();
|
|
|
|
|
// 添加主界面的Fragment到Activity中
|
|
|
|
|
addMainFragment();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initService(){
|
|
|
|
|
//启动服务
|
|
|
|
|
// 启动和绑定音乐播放相关服务(PlayerService和DownloadService)
|
|
|
|
|
private void initService() {
|
|
|
|
|
// 创建启动PlayerService的Intent
|
|
|
|
|
Intent playIntent = new Intent(MainActivity.this, PlayerService.class);
|
|
|
|
|
// 创建启动DownloadService的Intent
|
|
|
|
|
Intent downIntent = new Intent(MainActivity.this, DownloadService.class);
|
|
|
|
|
|
|
|
|
|
//退出程序后依然能播放
|
|
|
|
|
// 如果是Android 8.0及以上系统,以前台服务的方式启动PlayerService
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
|
startForegroundService(playIntent);
|
|
|
|
|
}else {
|
|
|
|
|
} else {
|
|
|
|
|
// Android 8.0以下,普通启动PlayerService
|
|
|
|
|
startService(playIntent);
|
|
|
|
|
}
|
|
|
|
|
// 绑定PlayerService,建立与服务的连接并获取Binder对象
|
|
|
|
|
bindService(playIntent, connection, Context.BIND_AUTO_CREATE);
|
|
|
|
|
// 绑定DownloadService,建立与服务的连接并获取Binder对象
|
|
|
|
|
bindService(downIntent, mDownloadConnection, Context.BIND_AUTO_CREATE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 接收OnlineSongErrorEvent事件的方法,在主线程中处理,这里只是简单弹出一个版权相关的提示Toast
|
|
|
|
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
|
|
|
public void onOnlineSongErrorEvent(OnlineSongErrorEvent event) {
|
|
|
|
|
showToast(getString(R.string.error_out_of_copyright));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 接收SongStatusEvent事件的方法,在主线程中处理,根据歌曲状态进行不同的界面和播放控制操作
|
|
|
|
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
|
|
|
public void onSongStatusEvent(SongStatusEvent event) {
|
|
|
|
|
int status = event.getSongStatus();
|
|
|
|
|
// 如果歌曲状态是恢复播放
|
|
|
|
|
if (status == Constant.SONG_RESUME) {
|
|
|
|
|
// 设置播放按钮为选中状态(表示正在播放)
|
|
|
|
|
mPlayerBtn.setSelected(true);
|
|
|
|
|
// 恢复歌曲封面图片的旋转动画
|
|
|
|
|
mCircleAnimator.resume();
|
|
|
|
|
// 启动SeekBar进度更新
|
|
|
|
|
seekBarStart();
|
|
|
|
|
} else if (status == Constant.SONG_PAUSE) {
|
|
|
|
|
// 设置播放按钮为未选中状态(表示暂停)
|
|
|
|
|
mPlayerBtn.setSelected(false);
|
|
|
|
|
// 暂停歌曲封面图片的旋转动画
|
|
|
|
|
mCircleAnimator.pause();
|
|
|
|
|
} else if (status == Constant.SONG_CHANGE) {
|
|
|
|
|
// 获取当前播放的歌曲对象(可能歌曲切换了)
|
|
|
|
|
mSong = FileUtil.getSong();
|
|
|
|
|
// 更新TextView显示的歌曲名称
|
|
|
|
|
mSongNameTv.setText(mSong.getSongName());
|
|
|
|
|
// 更新TextView显示的歌手名称
|
|
|
|
|
mSingerTv.setText(mSong.getSinger());
|
|
|
|
|
// 设置SeekBar的最大进度为歌曲的总时长
|
|
|
|
|
mSeekBar.setMax((int) mSong.getDuration());
|
|
|
|
|
// 设置播放按钮为选中状态(表示正在播放)
|
|
|
|
|
mPlayerBtn.setSelected(true);
|
|
|
|
|
// 启动歌曲封面图片的旋转动画
|
|
|
|
|
mCircleAnimator.start();
|
|
|
|
|
// 启动SeekBar进度更新
|
|
|
|
|
seekBarStart();
|
|
|
|
|
// 如果歌曲不是在线歌曲(可能是本地歌曲),通过CommonUtil工具类根据歌手名称设置默认图片
|
|
|
|
|
if (!mSong.isOnline()) {
|
|
|
|
|
CommonUtil.setSingerImg(MainActivity.this, mSong.getSinger(), mCoverIv);
|
|
|
|
|
} else {
|
|
|
|
|
// 如果是在线歌曲,使用Glide库加载歌曲封面图片,设置占位图和加载失败时显示的图片
|
|
|
|
|
Glide.with(MainActivity.this)
|
|
|
|
|
.load(mSong.getImgUrl())
|
|
|
|
|
.apply(RequestOptions.placeholderOf(R.drawable.welcome))
|
|
|
|
|
.apply(RequestOptions.errorOf(R.drawable.welcome))
|
|
|
|
|
.into(mCoverIv);
|
|
|
|
|
.load(mSong.getImgUrl())
|
|
|
|
|
.apply(RequestOptions.placeholderOf(R.drawable.welcome))
|
|
|
|
|
.apply(RequestOptions.errorOf(R.drawable.welcome))
|
|
|
|
|
.into(mCoverIv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化数据相关操作,这里暂时没有具体实现内容
|
|
|
|
|
@Override
|
|
|
|
|
protected void initData() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理各种点击事件的方法
|
|
|
|
|
@Override
|
|
|
|
|
protected void onClick() {
|
|
|
|
|
//进度条的监听事件
|
|
|
|
|
// 为SeekBar设置进度变化监听器,用于处理进度条拖动等操作
|
|
|
|
|
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
|
|
|
|
// 当进度改变时调用(拖动过程中会多次调用),这里暂时没做具体处理
|
|
|
|
|
@Override
|
|
|
|
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当开始拖动进度条时调用,设置标记防止与后台更新进度冲突
|
|
|
|
|
@Override
|
|
|
|
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
|
|
|
|
//防止在拖动进度条进行进度设置时与Thread更新播放进度条冲突
|
|
|
|
|
isChange = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当停止拖动进度条时调用,根据播放状态设置新的播放位置,并重新启动SeekBar进度更新
|
|
|
|
|
@Override
|
|
|
|
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
|
|
|
if (mPlayStatusBinder.isPlaying()) {
|
|
|
|
|
@ -278,7 +346,7 @@ public class MainActivity extends BaseActivity {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//控制按钮,播放,暂停
|
|
|
|
|
// 为播放/暂停按钮设置点击监听器,根据当前播放状态进行播放、暂停、恢复播放等操作
|
|
|
|
|
mPlayerBtn.setOnClickListener(v -> {
|
|
|
|
|
mMediaPlayer = mPlayStatusBinder.getMediaPlayer();
|
|
|
|
|
if (mPlayStatusBinder.isPlaying()) {
|
|
|
|
|
@ -292,7 +360,8 @@ public class MainActivity extends BaseActivity {
|
|
|
|
|
mMediaPlayer.seekTo(time * 1000);
|
|
|
|
|
isSeek = false;
|
|
|
|
|
}
|
|
|
|
|
} else {//退出程序后重新打开后的情况
|
|
|
|
|
} else {
|
|
|
|
|
// 退出程序后重新打开后的情况,根据歌曲是否在线选择不同的播放方式
|
|
|
|
|
if (FileUtil.getSong().isOnline()) {
|
|
|
|
|
mPlayStatusBinder.playOnline();
|
|
|
|
|
} else {
|
|
|
|
|
@ -302,71 +371,88 @@ public class MainActivity extends BaseActivity {
|
|
|
|
|
mMediaPlayer.seekTo((int) mSong.getCurrentTime() * 1000);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
//下一首
|
|
|
|
|
mNextIv.setOnClickListener(v -> {
|
|
|
|
|
if (FileUtil.getSong().getSongName() != null) mPlayStatusBinder.next();
|
|
|
|
|
if (mPlayStatusBinder.isPlaying()) {
|
|
|
|
|
mPlayerBtn.setSelected(true);
|
|
|
|
|
} else {
|
|
|
|
|
mPlayerBtn.setSelected(false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//点击播放栏,跳转到播放的主界面
|
|
|
|
|
mLinear.setOnClickListener(v -> {
|
|
|
|
|
if (FileUtil.getSong().getSongName() != null) {
|
|
|
|
|
Intent toPlayActivityIntent = new Intent(MainActivity.this, PlayActivity.class);
|
|
|
|
|
// 为下一首歌曲按钮设置点击监听器,点击时切换到下一首歌曲,并根据播放状态更新播放按钮状态
|
|
|
|
|
mNextIv.setOnClickListener(v -> {
|
|
|
|
|
// 如果当前有正在播放的歌曲(歌曲名称不为空),则调用PlayerService的next方法切换到下一首歌曲
|
|
|
|
|
if (FileUtil.getSong().getSongName()!= null) mPlayStatusBinder.next();
|
|
|
|
|
// 如果PlayerService正在播放歌曲,设置播放按钮为选中状态(表示正在播放),否则设置为未选中状态(表示暂停)
|
|
|
|
|
if (mPlayStatusBinder.isPlaying()) {
|
|
|
|
|
mPlayerBtn.setSelected(true);
|
|
|
|
|
} else {
|
|
|
|
|
mPlayerBtn.setSelected(false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//播放情况
|
|
|
|
|
if (mPlayStatusBinder.isPlaying()) {
|
|
|
|
|
Song song = FileUtil.getSong();
|
|
|
|
|
song.setCurrentTime(mPlayStatusBinder.getCurrentTime());
|
|
|
|
|
FileUtil.saveSong(song);
|
|
|
|
|
toPlayActivityIntent.putExtra(Constant.PLAYER_STATUS, Constant.SONG_PLAY);
|
|
|
|
|
} else {
|
|
|
|
|
//暂停情况
|
|
|
|
|
Song song = FileUtil.getSong();
|
|
|
|
|
song.setCurrentTime(mSeekBar.getProgress());
|
|
|
|
|
FileUtil.saveSong(song);
|
|
|
|
|
}
|
|
|
|
|
if (FileUtil.getSong().getImgUrl() != null) {
|
|
|
|
|
toPlayActivityIntent.putExtra(SearchContentFragment.IS_ONLINE, true);
|
|
|
|
|
}
|
|
|
|
|
//如果版本大于21
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
|
|
|
startActivity(toPlayActivityIntent,
|
|
|
|
|
ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle());
|
|
|
|
|
} else {
|
|
|
|
|
startActivity(toPlayActivityIntent);
|
|
|
|
|
}
|
|
|
|
|
// 为包含播放相关控件的LinearLayout设置点击监听器,点击时跳转到播放的主界面(PlayActivity),并传递相关播放状态等信息
|
|
|
|
|
mLinear.setOnClickListener(v -> {
|
|
|
|
|
// 如果当前有正在播放的歌曲(歌曲名称不为空),执行跳转操作
|
|
|
|
|
if (FileUtil.getSong().getSongName()!= null) {
|
|
|
|
|
Intent toPlayActivityIntent = new Intent(MainActivity.this, PlayActivity.class);
|
|
|
|
|
|
|
|
|
|
// 如果PlayerService正在播放歌曲,获取当前歌曲对象,设置当前播放时间,并将播放状态设置为正在播放(Constant.SONG_PLAY)传递给目标Activity
|
|
|
|
|
if (mPlayStatusBinder.isPlaying()) {
|
|
|
|
|
Song song = FileUtil.getSong();
|
|
|
|
|
song.setCurrentTime(mPlayStatusBinder.getCurrentTime());
|
|
|
|
|
FileUtil.saveSong(song);
|
|
|
|
|
toPlayActivityIntent.putExtra(Constant.PLAYER_STATUS, Constant.SONG_PLAY);
|
|
|
|
|
} else {
|
|
|
|
|
showToast(getString(R.string.welcome_start));
|
|
|
|
|
// 如果歌曲处于暂停状态,获取当前歌曲对象,将SeekBar当前进度作为歌曲当前播放时间设置给歌曲对象,并保存
|
|
|
|
|
Song song = FileUtil.getSong();
|
|
|
|
|
song.setCurrentTime(mSeekBar.getProgress());
|
|
|
|
|
FileUtil.saveSong(song);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 如果歌曲封面图片的URL不为空,说明是在线歌曲,传递一个标识(SearchContentFragment.IS_ONLINE为true)给目标Activity
|
|
|
|
|
if (FileUtil.getSong().getImgUrl()!= null) {
|
|
|
|
|
toPlayActivityIntent.putExtra(SearchContentFragment.IS_ONLINE, true);
|
|
|
|
|
}
|
|
|
|
|
// 如果系统版本大于等于LOLLIPOP(Android 5.0),使用转场动画启动目标Activity
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
|
|
|
startActivity(toPlayActivityIntent,
|
|
|
|
|
ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle());
|
|
|
|
|
} else {
|
|
|
|
|
// 如果系统版本小于Android 5.0,直接启动目标Activity
|
|
|
|
|
startActivity(toPlayActivityIntent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// 如果没有正在播放的歌曲,弹出一个提示Toast(显示默认的欢迎开始之类的文本)
|
|
|
|
|
showToast(getString(R.string.welcome_start));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加主界面的Fragment到Activity中的方法
|
|
|
|
|
private void addMainFragment() {
|
|
|
|
|
// 创建MainFragment实例
|
|
|
|
|
MainFragment mainFragment = new MainFragment();
|
|
|
|
|
// 获取FragmentManager对象,用于管理Fragment事务
|
|
|
|
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
|
|
|
|
// 开启Fragment事务
|
|
|
|
|
FragmentTransaction transaction = fragmentManager.beginTransaction();
|
|
|
|
|
// 将MainFragment添加到指定的布局容器(R.id.fragment_container)中
|
|
|
|
|
transaction.add(R.id.fragment_container, mainFragment);
|
|
|
|
|
// 提交Fragment事务,使添加操作生效
|
|
|
|
|
transaction.commit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 启动SeekBar进度更新的方法,创建并启动一个线程来更新SeekBar的进度显示
|
|
|
|
|
private void seekBarStart() {
|
|
|
|
|
mSeekBarThread = new Thread(new SeekBarThread());
|
|
|
|
|
mSeekBarThread.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 实现了Runnable接口的内部类,用于在后台线程中更新SeekBar的进度显示
|
|
|
|
|
class SeekBarThread implements Runnable {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
if (mPlayStatusBinder != null) {
|
|
|
|
|
// 只要没有正在拖动进度条且PlayerService正在播放歌曲,就持续更新SeekBar的进度
|
|
|
|
|
if (mPlayStatusBinder!= null) {
|
|
|
|
|
while (!isChange && mPlayStatusBinder.isPlaying()) {
|
|
|
|
|
// 设置SeekBar的进度为从PlayerService获取到的当前播放时间(单位可能需要和SeekBar的进度单位一致,这里看起来是以秒为单位)
|
|
|
|
|
mSeekBar.setProgress((int) mPlayStatusBinder.getCurrentTime());
|
|
|
|
|
try {
|
|
|
|
|
// 线程休眠1秒,模拟每秒更新一次进度
|
|
|
|
|
Thread.sleep(1000);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
@ -375,4 +461,4 @@ public class MainActivity extends BaseActivity {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|