Android MediaPlayer MP3播放器(倍速和音量)的封装和所见的问题

文章目录

  • Android MediaPlayer MP3播放器(倍速和音量)的封装和所见的问题
    • 一.技术选型
    • 二.为了方便调试
    • 三.实现步骤
      • 1.创建Library(也可以不创建,直接从demo中写也行)
      • 2.创建一个布局文件..(有点啰嗦了别嫌弃)
      • 3.Java类继承RelativeLayout(组合式自定义控件)
      • 4.设置结束监听回调的接口
      • 5.封面图使用的Glide圆形工具
      • 6.添加耳机连接的广播接收者
    • 四.出现的问题
      • 1.属性动画点暂停后再次点击开始将会从头开始播放
      • 2.进度条的进度问题
      • 3.华为手机设置倍速问题
      • 4.界面绘制出现过慢或者ANR
      • 5.解决完界面绘制出现过慢或者ANR发现获取音频长度不准确.....
      • 6.免积分下载地址 (记得点赞)

技术实现 咱要感谢谷歌大哥的mediaplayer,虽然有些问题挺难搞定,但是咱还是充满信心

功能包括

  1. MP3格式音乐的播放(MediaPlayer也支持其他格式 so:不解释了)
  2. MediaPlayer倍速
  3. MediaPlayer音量设置
  4. 进度条设置
  5. 结束 播放完成监听
  6. 背景和封面 背景高斯模糊(图片都是在线图片)
  7. 封面旋转动画(已解决从结束位置开始)
    等等…

运行效果


代码可能比较多,仔细阅读,转发请标明出处

一.技术选型

  1. MP3播放选择了谷歌大哥的MediaPlayer(本也考虑ijk,写着发现个弊端,倍速功能不支持6.0以下手机)
  2. 背景图和显示图片使用了Glide加载图片(包括高斯模糊效果)
  3. 旋转动画(重点是停止后继续播放当时确实头疼不好找)

简单介绍下开发背景(原因):

最近项目的第一个版本完成,第二个版本的迭代已经开始,产品经理剑走偏锋要求项目增加音频播放功能,包括音频的音量和倍速功能,优于之前并未做过该项功能,对于音频播放咱也是一知半解,于是乎我就开始对一些成功的开源项目进行了简单的研究,绝大多数都是基于mediaplayer进行了封装,源码咱的水平有些地方还是没有看懂,并且绝大多数是都是使用service还有混合广播等方式实现状态栏播放等,由于项目中并不需要这些需求.所以就自己动手写了个,咱写这个博客的时候功能基本完善. 还有啊我用的时间不长可能有些地方考虑不到位 请在评论区指出,thank you

添加的依赖
/glide 相关依赖/
implementation ‘com.github.bumptech.glide:glide:3.8.0’
//高斯模糊 Glide工具类性质的依赖 对性能消耗相对较小
implementation ‘jp.wasabeef:glide-transformations:2.0.1’

二.为了方便调试

为了方便自己调试(得亏是这么做的不然后边bug调试这块咱能头疼死,毕竟直接从项目中进行修改需要担点不大不小的风险)和修改减少耦合度就使用了依赖的方式进行开发
那咱创建Library的方式咱就先不说了

三.实现步骤

接下来呢咱就步入正题

1.创建Library(也可以不创建,直接从demo中写也行)

点击File(左上角)->选择new->选择new Modile->选择Android Library->然后起个中听的名字和顺眼的包名->finish就OK了(emmm)

2.创建一个布局文件…(有点啰嗦了别嫌弃)

咱没有起名字的天赋就先起个my_music_player.xml吧
关于这个布局嵌套的问题是因为刚开始就是用约束去写的但是发现无法撑开整个view但是时间紧迫所以就这么写了下, 那个 大家用的时候自己改一下…别嫌费事…

// my_music_player布局 一顿CV 起的名字是听难听
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000"><android.support.constraint.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/my_music_showpopup"android:layout_width="match_parent"android:layout_height="0.5dp"android:background="#0000"/><ImageViewandroid:id="@+id/my_music_background"android:layout_width="0dp"android:layout_height="0dp"android:scaleType="centerCrop"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><ImageViewandroid:id="@+id/my_music_image"android:layout_width="124dp"android:layout_height="124dp"android:padding="4dp"android:background="@drawable/musicicon"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"/><ImageViewandroid:id="@+id/my_music_start"android:layout_width="44dp"android:layout_height="44dp"android:src="@drawable/music_play_normal"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><SeekBarandroid:id="@+id/my_music_seek"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginLeft="88dp"android:layout_marginRight="88dp"android:layout_marginBottom="20dp"android:maxHeight="2dp"android:progressDrawable="@drawable/playerbg_music_seek_bar"android:thumb="@drawable/music_player_seek_btn"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent" /><LinearLayoutandroid:id="@+id/my_music_loaderror"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/music_play_errorbg"android:gravity="center"android:orientation="vertical"android:padding="10dp"android:visibility="invisible"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="音频加载失败"android:textColor="#fff"android:textSize="14sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:background="@drawable/music_play_errorbg"android:paddingLeft="10dp"android:paddingTop="5dp"android:paddingRight="10dp"android:paddingBottom="5dp"android:text="点击重试"android:textColor="#fff"android:textSize="12sp" /></LinearLayout><ImageViewandroid:layout_width="24dp"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:layout_marginRight="18dp"android:src="@drawable/music_icon_more"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent" /><ImageViewandroid:id="@+id/my_music_more"android:layout_width="44dp"android:layout_height="44dp"android:layout_marginRight="10dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent"/><TextViewandroid:id="@+id/my_music_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="00:00"android:textColor="#FFF"android:textSize="13sp"app:layout_constraintBottom_toBottomOf="@+id/my_music_seek"app:layout_constraintEnd_toStartOf="@+id/my_music_seek"app:layout_constraintTop_toTopOf="@+id/my_music_seek" /><TextViewandroid:id="@+id/my_music_endtime"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="00:00"android:textColor="#FFF"android:textSize="13sp"app:layout_constraintBottom_toBottomOf="@+id/my_music_seek"app:layout_constraintStart_toEndOf="@+id/my_music_seek"app:layout_constraintTop_toTopOf="@+id/my_music_seek" /></android.support.constraint.ConstraintLayout></RelativeLayout>

写出来就是这个样子了:

设置SeekBar的属性和封面图的边界

3.Java类继承RelativeLayout(组合式自定义控件)

本想写service去实现播放,由于时间问题,就直接写类里边去了

/*** 音乐播放器View*/
public class MyMusicPlayerView extends RelativeLayout {//控件public ImageView my_music_background;public ImageView my_music_image;public ImageView my_music_start;public SeekBar my_music_seek;public LinearLayout my_music_loaderror;public ImageView my_music_more;public TextView my_music_time;public TextView my_music_endtime;public TextView my_music_showpopup;//变量private boolean playNow;//切换后是否立即播放private int duration;//音频总长度private int handlerSeekTo = 0;private int seconds;//记录handle执行的次数和SeekBar最大长度private int secondsTemp;//用于保存变速后 一秒所代表的长度private HeadsetReceiver headsetReceiver; // 耳机连接广播private AudioManager audioManager;//播放管理类private int status = 0;private MediaPlayer mediaPlayer;private Context context;private String path;private ObjectAnimator myAnimator;private PopupWindow popupWindow;private int thisViewHeight;private int soundSize = 10;private int speedText = 10001;private MusicPlayerComplete musicPlayerComplete;private boolean isChanged = false;private String imageUrl;//常量private final int SEEK_MAX = 1000;private final int SECONDS_NOMAL_SPPED = 1000;private final int MEDIA_STATUS_ISNOTSTART = 0;//未开始播放private final int MEDIA_STATUS_ISSTART = 1;//已经开始播放private final int MEDIA_STATUS_ISEND = 2;//播放结束private final int MEDIA_STATUS_ISERROR = 3;//播放出错private final int MEDIA_URL_NULL = 4;//url为空private final float MEDIA_SPEED_1_0 = 1.0f;//一倍速private final float MEDIA_SPEED_1_25 = 1.25f;//一点二倍速private final float MEDIA_SPEED_1_5 = 1.5f;//一点五倍速private final float MEDIA_SPEED_2_0 = 2.0f;//二倍速private final int MEDIA_CHECKEDSPEED_1_0 = 10001;//一倍速 选中标识private final int MEDIA_CHECKEDSPEED_1_25 = 10002;//一点二倍速 选中标识private final int MEDIA_CHECKEDSPEED_1_5 = 10003;//一点五倍速 选中标识private final int MEDIA_CHECKEDSPEED_2_0 = 10004;//二倍速 选中标识/*** 进度条*/Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {my_music_seek.setProgress(handlerSeekTo);//修改进度到---handlerSeekTo++;//进度调 进度if (handlerSeekTo >= seconds) {handler.removeCallbacksAndMessages(null);handlerSeekTo = seconds;status = MEDIA_STATUS_ISEND;my_music_start.setImageResource(R.drawable.music_restart_normal);stopAnimation();//播放完成监听 外部方法if (musicPlayerComplete != null) {musicPlayerComplete.musicComplete();}} else {handler.sendEmptyMessageDelayed(1, secondsTemp);}my_music_time.setText(getTime(handlerSeekTo * SECONDS_NOMAL_SPPED));return false;}});public MyMusicPlayerView(Context context) {this(context, null);}public MyMusicPlayerView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyMusicPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.context = context;findView(context);initAudioManager();initReceiver(context);}private void initAudioManager() {audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);}/*** 初始化耳机连接监听** @param context*/private void initReceiver(Context context) {headsetReceiver = new HeadsetReceiver();IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);context.registerReceiver(headsetReceiver, intentFilter);headsetReceiver.setOnHeadsetDetectLintener(new HeadsetReceiver.OnHeadsetDetectLintener() {@Overridepublic void isHeadseDetectConnected(boolean connected) {if (connected) {//耳机已连接 do something...changeToHeadset();Log.e("外音播放", "" + connected);} else {//耳机已断开 do something...changeToSpeaker();Log.e("外音播放", "" + connected);}}});}/*** 寻找布局 并初始化需要的东西** @param context 上下文*/private void findView(Context context) {View view = View.inflate(context, R.layout.my_music_player, this);initView(view);initMediaPlayer();initListener(view);initAnimation();}/*** 初始化View** @param view 布局*/private void initView(View view) {my_music_background = (ImageView) view.findViewById(R.id.my_music_background);my_music_image = (ImageView) view.findViewById(R.id.my_music_image);my_music_start = (ImageView) view.findViewById(R.id.my_music_start);my_music_seek = (SeekBar) view.findViewById(R.id.my_music_seek);my_music_loaderror = (LinearLayout) view.findViewById(R.id.my_music_loaderror);my_music_more = (ImageView) view.findViewById(R.id.my_music_more);my_music_time = (TextView) view.findViewById(R.id.my_music_time);my_music_endtime = (TextView) view.findViewById(R.id.my_music_endtime);my_music_showpopup = (TextView) view.findViewById(R.id.my_music_showpopup);my_music_seek.setMax(SEEK_MAX);}/*** 初始化监听** @param view 布局*/private void initListener(View view) {//播放进度my_music_seek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}//手动改变进度@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {int progress = my_music_seek.getProgress();mediaPlayer.seekTo(progress * 1000);handlerSeekTo = progress;}});//播放 暂停 重播my_music_start.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {SelectStatus();}});//播放出错mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {@Overridepublic boolean onError(MediaPlayer mp, int what, int extra) {status = MEDIA_STATUS_ISERROR;my_music_start.setImageResource(R.drawable.music_restart_normal);handler.removeCallbacksAndMessages(null);stopAnimation();return false;}});/*//获取当前控件高度my_music_background.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {if (Build.VERSION.SDK_INT >= 16) {my_music_background.getViewTreeObserver().removeOnGlobalLayoutListener(this);} else {my_music_background.getViewTreeObserver().removeGlobalOnLayoutListener(this);}}});*///点击更多功能my_music_more.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {thisViewHeight = my_music_background.getHeight();showPoupWind();}});}/*** 初始化 MediaPlayer*/private void initMediaPlayer() {mediaPlayer = new MediaPlayer();if (TextUtils.isEmpty(path)) {status = MEDIA_URL_NULL;} else {status = MEDIA_STATUS_ISNOTSTART;}}/*** 播放结束 接口回调** @param musicPlayerComplete 接口*/public void setMusicPlayerComplete(MusicPlayerComplete musicPlayerComplete) {this.musicPlayerComplete = musicPlayerComplete;}/*** 展示更多功能弹框*/private void showPoupWind() {popupWindow = new PopupWindow(this);popupWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);popupWindow.setHeight(thisViewHeight);View popupVeiw = LayoutInflater.from(context).inflate(R.layout.my_music_popup, null);popupWindow.setContentView(popupVeiw);popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));popupWindow.setOutsideTouchable(false);popupWindow.setFocusable(true);popupWindow.showAsDropDown(my_music_showpopup);final RadioGroup my_music_bsgroup = (RadioGroup) popupVeiw.findViewById(R.id.my_music_bsgroup);SeekBar my_music_sound = (SeekBar) popupVeiw.findViewById(R.id.my_music_sound);//上一次popupwindow打开时的音量和倍速my_music_bsgroup.check(getCheckedId());my_music_sound.setProgress(soundSize);changedCheckedColor(my_music_bsgroup, getCheckedId());//radiobutton 选中my_music_bsgroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {if (checkedId == R.id.my_music_bsbtn1) {speedText = MEDIA_CHECKEDSPEED_1_0;changedSpeed(MEDIA_SPEED_1_0);} else if (checkedId == R.id.my_music_bsbtn1_25) {speedText = MEDIA_CHECKEDSPEED_1_25;changedSpeed(MEDIA_SPEED_1_25);} else if (checkedId == R.id.my_music_bsbtn1_5) {speedText = MEDIA_CHECKEDSPEED_1_5;changedSpeed(MEDIA_SPEED_1_5);} else if (checkedId == R.id.my_music_bsbtn2) {speedText = MEDIA_CHECKEDSPEED_2_0;changedSpeed(MEDIA_SPEED_2_0);}changedCheckedColor(my_music_bsgroup, checkedId);}});//播放音量my_music_sound.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {soundSize = seekBar.getProgress();float soundSize = progress * 0.1f;mediaPlayer.setVolume(soundSize, soundSize);}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});//点击背景关闭弹框popupVeiw.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {popupWindow.dismiss();}});}/*** 改变选中btn颜色** @param group     单选按钮的组* @param checkedId 组内选中的id*/private void changedCheckedColor(RadioGroup group, int checkedId) {for (int i = 0; i < group.getChildCount(); i++) {if (i != 0) {int id = group.getChildAt(i).getId();RadioButton childAt = (RadioButton) group.getChildAt(i);if (id == checkedId) {childAt.setTextColor(Color.rgb(216, 176, 118));} else {childAt.setTextColor(Color.rgb(255, 255, 255));}}}}/*** 倍速选中** @return view的id*/private int getCheckedId() {int checkedId = R.id.my_music_bsbtn1;switch (speedText) {case MEDIA_CHECKEDSPEED_1_0:return checkedId;case MEDIA_CHECKEDSPEED_1_25:checkedId = R.id.my_music_bsbtn1_25;return checkedId;case MEDIA_CHECKEDSPEED_1_5:checkedId = R.id.my_music_bsbtn1_5;return checkedId;case MEDIA_CHECKEDSPEED_2_0:checkedId = R.id.my_music_bsbtn2;return checkedId;}return checkedId;}/*** 设置 MP3地址 和背景图地址** @param path     Mp3的路径* @param imageUrl 封面图路径*/public void setUp(String path, String imageUrl) {this.path = path;//播放地址this.imageUrl = imageUrl;try {if (TextUtils.isEmpty(path)) {//播放地址无效status = MEDIA_URL_NULL;} else {//有效播放地址Uri parse = Uri.parse(path);/*mediaPlayer.setDataSource(context, parse);*/mediaPlayer = new MediaPlayer();mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mediaPlayer.setDataSource(context, parse);mediaPlayer.prepareAsync();Log.e("总时长", "" + mediaPlayer.getDuration());mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {try {/*     mediaPlayer.prepare();*/status = MEDIA_STATUS_ISNOTSTART;//音频总长度duration = mediaPlayer.getDuration();//seekbar最大长度seconds = duration / SEEK_MAX;my_music_seek.setMax(seconds);//seekbar每一次更新的时间String time = getTime(duration);my_music_endtime.setText(time);if (isChanged && playNow) {MediaStart();} else {secondsTemp = SECONDS_NOMAL_SPPED;}isChanged = false;playNow = false;} catch (Exception e) {e.printStackTrace();}}});}} catch (IOException e) {e.printStackTrace();}//高斯模糊背景Glide.with(context).load(TextUtils.isEmpty(imageUrl) ? R.drawable.music_play_icon : imageUrl).dontAnimate().bitmapTransform(new BlurTransformation(context, 40, 3)).into(my_music_background);//音乐封面Glide.with(context).load(TextUtils.isEmpty(imageUrl) ? R.drawable.music_play_icon : imageUrl).dontAnimate().transform(new GlideCircleUtil(context)).into(my_music_image);}/*** 将int值转换成时长** @return 时长*/private String getTime(int durations) {int time = durations / 1000;int temp;StringBuffer sb = new StringBuffer();temp = time / 3600;if (temp != 0) {//时长不超过一小时则不添加sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");}temp = time % 3600 / 60;sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");temp = time % 3600 % 60;sb.append((temp < 10) ? "0" + temp : "" + temp);return sb.toString();}/*** 播放状态 选中状态*/private void SelectStatus() {startMyAnimation();switch (status) {case MEDIA_URL_NULL:Toast.makeText(context, "无效播放地址", Toast.LENGTH_SHORT).show();my_music_start.setImageResource(R.drawable.music_play_normal);stopAnimation();break;case MEDIA_STATUS_ISNOTSTART:MediaStart();break;case MEDIA_STATUS_ISSTART:MediaPause();break;case MEDIA_STATUS_ISEND:MediaStart();break;case MEDIA_STATUS_ISERROR:MediaStart();my_music_start.setVisibility(INVISIBLE);my_music_loaderror.setVisibility(VISIBLE);break;}}/*** 暂停播放*/public void MediaPause() {mediaPlayer.pause();status = MEDIA_STATUS_ISNOTSTART;my_music_start.setVisibility(VISIBLE);my_music_loaderror.setVisibility(INVISIBLE);my_music_start.setImageResource(R.drawable.music_play_normal);handler.removeCallbacksAndMessages(null);stopAnimation();}/*** 音频 开始播放 继续播放 重新播放*/public void MediaStart() {if (handlerSeekTo >= seconds) {handlerSeekTo = 0;}status = MEDIA_STATUS_ISSTART;my_music_start.setImageResource(R.drawable.music_pause_normal);my_music_start.setVisibility(VISIBLE);my_music_loaderror.setVisibility(INVISIBLE);mediaPlayer.start();handler.removeCallbacksAndMessages(null);handler.sendEmptyMessageDelayed(1, 0);startMyAnimation();//播放完成mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {}});}/*** 播放的地址发生改变** @param musicUrl 播放地址* @param playNow  是否立即播放*/public void MediaChanged(String musicUrl, boolean playNow) {my_music_time.setText("00:00");my_music_endtime.setText("00:00");speedText = MEDIA_CHECKEDSPEED_1_0;isChanged = true;this.playNow = playNow;this.setUp(musicUrl, imageUrl);}/*** 释放内存*/public void MediaClear() {try {mediaPlayer.release();handler.removeCallbacksAndMessages(null);my_music_time.setText("00:00");mediaPlayer = null;handlerSeekTo = 0;my_music_seek.setProgress(0);this.getContext().unregisterReceiver(headsetReceiver);} catch (Exception e) {Log.e("清理MediaPlayer", "" + e);}}/*** 播放速度** @param speed*/private void changedSpeed(float speed) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (!TextUtils.isEmpty(path)) {mediaPlayer.setPlaybackParams(new PlaybackParams().setSpeed(speed));String speedString = "已切换为<font color='#D8B076'>" + speed + "</font>倍速度播放";new CenterToast(this.getContext(), speedString);secondsTemp = (int) (SECONDS_NOMAL_SPPED / speed);mediaPlayer.pause();//因为华为等机型的问题 在改变倍速后必须要这么写 先暂停再开始才能倍速播放MediaStart();} else {speedText = MEDIA_CHECKEDSPEED_1_0;Toast.makeText(context, "无效播放地址", Toast.LENGTH_SHORT).show();}}}/*** 初始化 动画*/private void initAnimation() {myAnimator = ObjectAnimator.ofFloat(my_music_image, "rotation", 0f, 360f);myAnimator.setDuration(25000);myAnimator.setRepeatCount(-1);myAnimator.setInterpolator(new LinearInterpolator());myAnimator.start();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {myAnimator.pause();} else {myAnimator.cancel();}}/*** 开始动画*/private void startMyAnimation() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {myAnimator.resume();} else {myAnimator.start();}}/*** 停止动画*/private void stopAnimation() {if (myAnimator != null) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {myAnimator.pause();} else {myAnimator.cancel();}}}/*** 切换到外放*/public void changeToSpeaker() {audioManager.setMode(AudioManager.MODE_NORMAL);audioManager.setSpeakerphoneOn(true);}/*** 切换到耳机模式*/public void changeToHeadset() {audioManager.setSpeakerphoneOn(false);}}

4.设置结束监听回调的接口

public interface MusicPlayerComplete {void musicComplete();//仅仅用于结束监听
}

5.封面图使用的Glide圆形工具

本来是不想着放的 想了想还是放吧 其实也没啥意义 主要是Glide比较新的版本都可以直接整圆了 我因为项目的问题暂时还用着较老版本

public class GlideCircleUtil extends BitmapTransformation {public GlideCircleUtil(Context context) {super(context);}@Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {return circleCrop(pool, toTransform);}private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {if (source == null) return null;int size = Math.min(source.getWidth(), source.getHeight());int x = (source.getWidth() - size) / 2;int y = (source.getHeight() - size) / 2;// TODO this could be acquired from the pool tooBitmap squared = Bitmap.createBitmap(source, x, y, size, size);Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);if (result == null) {result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);}Canvas canvas = new Canvas(result);Paint paint = new Paint();paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));paint.setAntiAlias(true);float r = size / 2f;canvas.drawCircle(r, r, r, paint);return result;}@Override public String getId() {return getClass().getName();}
}

我刚开始写到这里就感觉已经完成了
但是提交后产品给提了个 插上耳机耳机无声音
其实上边代码可以看到一个角耳机连接广播的类是红色的 这个是因为Android没有自己处理 咱只能通过广播去监听耳机连接状态 SO :

6.添加耳机连接的广播接收者

public class HeadsetReceiver extends BroadcastReceiver {private OnHeadsetDetectLintener onHeadsetDetectLintener;@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (Intent.ACTION_HEADSET_PLUG.equals(action)) {if (intent.hasExtra("state")) {int state = intent.getIntExtra("state", 0);if (state == 1) {//插入耳机onHeadsetDetectLintener.isHeadseDetectConnected(true);} else if (state == 0) {//拔出耳机onHeadsetDetectLintener.isHeadseDetectConnected(false);}}}}public interface OnHeadsetDetectLintener {void isHeadseDetectConnected(boolean connected);}public void setOnHeadsetDetectLintener(OnHeadsetDetectLintener onHeadsetDetectLintener) {this.onHeadsetDetectLintener = onHeadsetDetectLintener;}
}

到这里封装基本完成了就 代码量不多 可是当时也是耗费了一天的时间
接下来就是标准的流程了–总结 — 发现个问题好像每一篇博客的长度都是有限制的

四.出现的问题

别看代码不多,一些值得注意值得记下来的问题倒是不少

1.属性动画点暂停后再次点击开始将会从头开始播放

属性动画,确实也好用但是会出现一个比较普遍的问题就是结束后停留在指定位置,再次开始将会从头开始,如果是我这中微强迫症还能捏着鼻子接收,就是微微的不爽,但是对于一些强迫症比较强的人来说就能难受死,所以啊全网找方法,最终! 功夫不负有心人找到了.但是也是有弊端的,就是对Android版本有一定的要求,要是有大佬有更好的解决方案,请告知

/*** 初始化 动画*/private void initAnimation() {myAnimator = ObjectAnimator.ofFloat(my_music_image, "rotation", 0f, 360f);myAnimator.setDuration(25000);myAnimator.setRepeatCount(-1);myAnimator.setInterpolator(new LinearInterpolator());myAnimator.start();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {myAnimator.pause();} else {myAnimator.cancel();}}/*** 开始动画*/private void startMyAnimation() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {myAnimator.resume();} else {myAnimator.start();}}/*** 停止动画*/private void stopAnimation() {if (myAnimator != null) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {myAnimator.pause();} else {myAnimator.cancel();}}}

2.进度条的进度问题

在刚开始的时候我吧进度条设置为了1000的长度,在播放时间相对较短的音频的时候还没有什么问题,但是我加载了一个我们公司时长为1:32:00的音频的时候发现进度条每好几秒(也是给忘了几秒了)移动一个单位…这种情况肯定是不行的,于是就讲handler的发送时间调整为了1000(毫秒),以整秒发送,音频总长度比上1000就是handler执行的次数和进度条的最大长度,

3.华为手机设置倍速问题

mediaplayer在播放的时候需要添加倍速功能,于是就开始找倍速方法和代码,发现好简单啊,写完后提交功能后经测试,发现在三款华为手机上皆有问题,mediaplayer正常播放没有问题,但是!一旦切换倍速功能就会出现问题,只要点击倍速功能 哪怕是正常播放(就是一倍速)点击了一倍速播放那也会进入无声状态…

倍速接口说明:
(1) 使用这个接口可以进行播放速率的设置。
(2) 播放器prepared状态之前调用这个方法不会更改播放器的状态。
(3) prepared状态之后设置速率0等同于调用pause(),当调用start恢复播放以后,将以原来的速率进行播放。
(4) prepared状态之后设置非0的速率等同于调用start()。
(5) 当播放器还未初始化或者已经被释放的时候设置会抛IllegalStateException的异常。
(6) 当参数不支持的时候会抛IllegalArgumentException的异常。

设置时机要求:
合法的时机:Initialized, Prepared, Started, Paused, PlaybackCompleted, Error
非法的时机:Idle, Stopped

4.界面绘制出现过慢或者ANR

主要是因为音频mediaPlayer.prepare();方法

原因:一般在线音频播放是不使用mediaPlayer.prepare()而是使用mediaPlayer.prepareAsync()方法
prepare方法是将资源同步缓存到内存中,一般加载本地较小的资源可以用这个,如果是较大的资源或者网络资源建议使用prepareAsync方法,异步加载.但如果想让资源启动,即start()起来,因为在异步中,如果不设置监听直接start的话,是拿不到这个资源,如果让线程睡眠一段时间,则可以取得资源,因为这个时候,异步线程已经取得资源,但不可能使用线程睡眠的方式来获取资源啊.所以就需要设置监听事件setOnPreparedListener();来通知MediaPlayer资源已经获取到了,然后实现onPrepared(MediaPlayer mp)方法.在里面启动MediaPlayer
这句摘抄自(https://blog.csdn.net/qq_24223073/article/details/69315856)

不管咋,确实人家说的对 ,咱认同 按照所说改了后也就好使了

5.解决完界面绘制出现过慢或者ANR发现获取音频长度不准确…

刚开始写的加载音频方法是:mediaPlayer.prepare();
使用这个方法获取音频时长是正确的.
因为使用prepare方法会导致界面加载过慢等问题就给替换成了prepareAsync,运行时发现某些机型上获取音频的总时长要小于正常音频时长,也有些会多一些(获取的时长短了咱能理解,边长那就不明白了)
我之前的写法是

mediaPlayer.prepareAsync();
//音频总长度
duration = mediaPlayer.getDuration();

找了一系列方法发现此方法是最有效的一个方法:

//此方法要放到mediaPlayer.prepareAsync()之后不然无效
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {//获取时长}
});

至此呢基本已经解决了问题,有些问题解决了 没想起来就没有往里边写

6.免积分下载地址 (记得点赞)

下载链接这个音频播放器(虽然称不上播放器)并没有创建单独的一个工作空间区备份,就先和视频播放器在一个工作空间了,视频播放器不是咱自己写的,是基于饺子视频播放器改的

Android MediaPlayer MP3播放器(倍速和音量)的封装和所见的问题相关推荐

  1. android实现MP3播放器

    android实现MP3播放器 前一段时间考试,这个做好的项目一直没有时间总结.虽然在做项目的期间,把用到的各种技术都记录下来写成blog了,但还是应该有一个总体上的概括与总结. 这是自己做的第一个比 ...

  2. 安卓java自实现mp3播放器,基于安卓Android的MP3播放器的设计与实现

    基于安卓Android的MP3播放器的设计与实现(含开题报告,毕业论文14000字,程序代码) [摘  要]    本课题致力于开发出一款基于Android手机平台的MP3音乐播放器应用,采用Andr ...

  3. 酷播云H5播放器倍速播放功能

    大家好,我是小酷,小酷今天来说说H5播放器倍速播放功能: 酷播云是支持HTML5播放器及Flash播放器,两者兼容,可自由切换.其中,H5播放器自带倍速播放功能,用户可选择视频的播放速度. 酷播云Ht ...

  4. android开发--mp3播放器项目源代码(xml文件解析,.lrc,.mp3文件下载,同时显示歌词)

    一.mp3播放器源代码 1.MainActivity.java:在此中主要负责播放器首页的功能,包括服务器上的下载列表,和SD卡上已经下载的mp3文件列表package com.wyt.MP3play ...

  5. Android MediaPlayer 音乐播放器扫描 本地音乐、上一曲、下一曲切歌、播放本地音乐

    Android MediaPlayer 本地音乐播放器 运行截图 项目请在真机(自己的手机)上测试运行,因为我不喜欢用虚拟机. 为了不浪费您的时间,先看一下运行的效果图, 一进去先进行音乐扫描,然后列 ...

  6. 由“播放器倍速”引发的ExoPlayer实践

    第一阶段: 为了实现"倍速功能",百度设置mediaPlayer的setPlaybackParams(PlaybackParams params) 接口说明: (1) 使用这个接口 ...

  7. html原生音频播放器倍速,HTML5倍数功能视频播放器(加速2倍,1.5倍播放)

    HTML5倍数功能视频播放器(加速2倍,1.5倍播放) 倍数功能视频播放器的应用: 培训场景,教育教学场景下,可以倍速观看视频,比如1.5倍,2倍的速度观看视频,可以更快速的看完课程,节省时间. va ...

  8. JavaScript设置HTML5播放器倍速播放

    文章目录 问题描述 解决方法 问题描述 今天,我在看一个学习视频时,发现这个网站的视频播放器居然没有倍速播放的按钮.岂有此理,作为一个长期以来一直用2倍速观看学习视频的强迫症患者,这令我十分不爽,因为 ...

  9. android b站倍速播放器,倍速功能(setPlaybackSpeed(float speed)) ,在安卓23以下机型,开启倍速后,音调变高。...

    看了ijk 源码发现,在安卓23以下的机型,使用了AudioTrack的setPlaybackRate()方法来改变声音的速度,但是这个方法同时会改变声音的音调. 所以我说的不对,这个和机型没什么关系 ...

最新文章

  1. 关于visualizer的setEnabled()方法何时进行设置成false?
  2. [翻译]一步步教你配置SQL SERVER合并复制(四)提高Distributor的安全性
  3. linux环境安装 kafka 0.8.2.1 jdk1.6
  4. idea 2018.1 创建springboot开启找回Run Dashboard
  5. 计算机二级excel数据有效性,原来Excel数据有效性还可以这样做——制作二级下拉菜单...
  6. OpenCV阶段总结扩充。
  7. 数据结构学习笔记:实现链表
  8. adb 51 android.rules,使用51-android-rules解决ubuntu上不识别 android手机的问题
  9. SpringBoot注解@Value取值取不到问题
  10. 台式电脑计算机能创建新磁盘吗,解决方案:如何添加硬盘以扩展台式计算机上的存储空间|如何对新添加的硬盘进行分区...
  11. 从业务到技术weibo link card快速接入思考-2014.09.20
  12. cstring判断包含字符串_Power Query中判断字符串中是否包含有字母的三种解决办法...
  13. 软件安全课程设计:高校科研管理系统
  14. LINUX命令 VS DOS命令
  15. 阿里矢量图标的三种使用方式整理(uniapp)
  16. Moviebooking电影售票系统--用例建模
  17. GoldWave音频混合剪辑教程
  18. 神经网络分析教学目标,神经网络分析教学反思
  19. 网站Logo SEO优化
  20. 杜克大学计算机世界排名,国本美本多背景的他竟拿杜克大学保底!轻松收获21年CS录取!...

热门文章

  1. 程序帝国四大操作之单表修改
  2. 谷歌身份验证器代码实现
  3. pycharm导入皮肤jar包方式
  4. python发微信语音没声音怎么回事_我的微信发语音没声音怎么回事
  5. AnyCasting.2.4[铸造模拟软件最新+天喻CAD2005完美
  6. 天猫延迟发货的定义是什么?延迟发货商家如何赔付?
  7. 华为3C刷机ROOT 一键ROOT必备工具
  8. 数字调制解调—MSK
  9. 验证不能输入中文的正则表达式
  10. JS Worker多线程