本文将详细解析音频播放器UI页面的核心实现要点,包括网络、封面动画、播放状态管理等关键技术细节,为开发者提供实用参考。
背景:该播放器支持专辑播放功能,具备歌曲列表、播放控制、收藏、倍速调节等常见功能,与主流音乐应用功能相似。

1、网络权限与实现
"android.permission.INTERNET" />IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(networkReceiver, filter, Context.RECEIVER_EXPORTED);
}else {
registerReceiver(networkReceiver, filter);
}
boolean isNetWork = false;private BroadcastReceiver networkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
isNetWork = JxwApplication.isNetwork();
if(isNetWork){ //有网
if (netWorkDialog != null && netWorkDialog.isShowing()) {
netWorkDialog.dismiss();//隐藏无网弹窗
}
}else {//无网
if(netWorkDialog!=null){
netWorkDialog.show(); //显示无网弹窗
}
animatorSet.pause(); //暂停封面旋转
}
}
}
};
2、专辑封面旋转动画
AnimatorSet animatorSet;
private String book_cover; //专辑封面//==========在onCreate 中===================
RequestOptions options = new RequestOptions();//图片圆角
Glide.with(ReadTextActivity.this).load(book_cover)
.placeholder(R.mipmap.img_med_player_placeholder) // 占位图资源ID
.error(R.mipmap.img_med_player_placeholder) // 错误占位图资源ID
.apply(options).into(mBinding.ivCover);animatorSet = new AnimatorSet();
ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(mBinding.ivCover, "rotation", 0f, 360f);
rotateAnimator.setInterpolator(new LinearInterpolator());
rotateAnimator.setDuration(10000);
rotateAnimator.setRepeatCount(Animation.INFINITE);
rotateAnimator.setRepeatMode(ValueAnimator.RESTART);
animatorSet.playTogether(rotateAnimator);
animatorSet.start();
animatorSet.pause();//====================播放====================
animatorSet.resume();
//====================暂停====================
animatorSet.pause();//==========在onDestroy 中===================
if (animatorSet != null) {
animatorSet.cancel();
animatorSet = null;
}
3、播放状态变量管理
private String book_cover; //专辑封面
private String book_name;
private int bookId;
private boolean isCollect = false; //是否收藏
private String unit_name; //正在播放的歌曲名称
public static float speed = 1f; //预选 播放速度
public static int loop_type = 0; // 0顺序播放 1单篇循环 2随机播放
public static boolean isplay = true; //播放状态
private String jsonList; //播放的歌曲列表数据
private int pre_gb_time = 0; //定时播放的时间 0/15/30/45分钟
4、基于EventBus的消息通信机制
//引用
implementation 'org.greenrobot:eventbus:3.2.0'//=============在onCreate() 中注册===============
EventBus.getDefault().register(this);//=============在onDestroy()中注销===============
// 针对性移除所有粘性事件类型(与订阅方法对应)
EventBus.getDefault().removeStickyEvent(PlayTimeShowMsg.class);
EventBus.getDefault().removeStickyEvent(PoiToUIMsg.class);
EventBus.getDefault().removeStickyEvent(PlayUnitPoiMsg.class);
EventBus.getDefault().removeStickyEvent(PlayShowPoiMsg.class);
EventBus.getDefault().removeStickyEvent(PlayStateMsg.class);
EventBus.getDefault().removeStickyEvent(RequestMsg.class);
EventBus.getDefault().removeStickyEvent(PlayInfoMsg.class);
// 最后注销订阅
EventBus.getDefault().unregister(this);//===========发送播放消息============
//封装成工具类 可从任何地方调用 比如通知栏或控制栏的自定义UI中实现控制播放
public class PlayControlMsg { public static final String YYTS_ACTION_PAUSE = "YYTS_ACTION_PAUSE";//暂停
public static final String YYTS_ACTION_PLAY = "YYTS_ACTION_PLAY";//播放
public static final String YYTS_PLAY_NEXT = "YYTS_PLAY_NEXT";//播放下一个
public static final String YYTS_PLAY_PREVIOUS = "YYTS_PLAY_PREVIOUS";//播放上一个 public String msg; public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
}
}
public class PlayControlTools { public static void setACTION_PAUSE(){//暂停
PlayControlMsg msg = new PlayControlMsg();
msg.setMsg(PlayControlMsg.YYTS_ACTION_PAUSE);
EventBus.getDefault().post(msg);
} public static void setACTION_PLAY(){//播放
PlayControlMsg msg = new PlayControlMsg();
msg.setMsg(PlayControlMsg.YYTS_ACTION_PLAY);
EventBus.getDefault().post(msg);
} public static void setPLAY_PREVIOUS(){ //上一个
PlayControlMsg msg = new PlayControlMsg();
msg.setMsg(PlayControlMsg.YYTS_PLAY_PREVIOUS);
EventBus.getDefault().post(msg);
} public static void setPLAY_NEXT(){ //下一个
PlayControlMsg msg = new PlayControlMsg();
msg.setMsg(PlayControlMsg.YYTS_PLAY_NEXT);
EventBus.getDefault().post(msg);
}
}//UI页滑动进度的 和消息发送
mBinding.seekbar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// Log.e("TAG","进度条,当前值:"+seekBar.getProgress());
// mBinding.ivReadBf.setImageResource(R.mipmap.icon_pause);
PoiToPlayMsg.sendMsg(seekBar.getProgress()); //发送指针
}
});
//=============接收播放消息================
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveTime(PlayTimeShowMsg msg) { //接收时间显示 定时播放的倒计时
mBinding.tvPlayTime.setText(ConstantFd.FormatMiss(msg.getTime()));
} @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveProcess(PoiToUIMsg msg) { //接收进度
// Log.i("TAG", "process: "+msg.getPoi());
// Log.i("TAG", "getMax: "+msg.getMax());
mBinding.seekbar1.setMax((int)msg.getMax());
mBinding.seekbar1.setProgress((int)msg.getPoi());
if(msg.getPoi()<=msg.getMax()){
//将int类型 进度转为String的 为分:秒 显示
mBinding.tvNum1.setText(ConstantFd.FormatMiss(msg.getPoi()));
}else {
mBinding.tvNum1.setText(ConstantFd.FormatMiss(msg.getMax()));
}
mBinding.tvNum2.setText(ConstantFd.FormatMiss(msg.getMax()));
} @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receivePlayPoi(PlayUnitPoiMsg msg) { //接收单元指针
Log.i("TAG", "单元指针poi: "+msg.getPoi());
mUnitAdapter.mPosSomeTime = msg.getPoi(); //适配器播放指针更新
getCollectState(); //获取收藏状态
updatePlayUI(); //刷新列表ui
playOrPauseUI(); //播放ui和动画更新
mBinding.recyclerView.smoothScrollToPosition(msg.getPoi()); //列表滚动到指定位置
} @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receivePlayPoi(PlayShowPoiMsg msg) { //接收单元指针
Log.i("TAG", "单元指针poi: "+msg.getPoi());
mUnitAdapter.mPosSomeTime = msg.getPoi();//适配器播放指针更新
} @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receivePlayState(PlayStateMsg msg) { //接收播放状态
playOrPauseUI(); //播放状态ui更新
} @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveEventBus(RequestMsg msg) { //数据请求中
if(!isFinishing()){
mBinding.loading.setVisibility(View.VISIBLE);
}
} @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveEventBus(PlayInfoMsg msg) {
mBinding.loading.setVisibility(View.GONE);
if(ConstantFd.unit_List.size() > 0 && mUnitAdapter!=null){
mUnitAdapter.updateData(ConstantFd.unit_List);//适配器数据更新
updatePlayUI(); //更新播放器ui 指针显示等
} }//================消息封装类===================
/**
* 歌曲播放进度
*/
public class PoiToPlayMsg { //UI->服务
long poi; //当前指针 public long getPoi() {
return poi;
} public void setPoi(long poi) {
this.poi = poi;
}
public static void sendMsg(long poi){
PoiToPlayMsg msg = new PoiToPlayMsg();
msg.setPoi(poi);
EventBus.getDefault().post(msg); //发送指针
}
}/**
* 歌曲播放进度
*/
public class PoiToUIMsg { //服务->UI
long poi; //当前指针
long max; //最大指针 public long getPoi() {
return poi;
} public void setPoi(long poi) {
this.poi = poi;
} public long getMax() {
return max;
} public void setMax(long max) {
this.max = max;
} public static