Android MediaPlayer MP3播放器(倍速和音量)的封装和所见的问题
技术实现 咱要感谢谷歌大哥的mediaplayer,虽然有些问题挺难搞定,但是咱还是充满信心
功能包括
- MP3格式音乐的播放(MediaPlayer也支持其他格式 so:不解释了)
- MediaPlayer倍速
- MediaPlayer音量设置
- 进度条设置
- 结束 播放完成监听
- 背景和封面 背景高斯模糊(图片都是在线图片)
- 封面旋转动画(已解决从结束位置开始)
等等…
运行效果
代码可能比较多,仔细阅读,转发请标明出处
一.技术选型
- MP3播放选择了谷歌大哥的MediaPlayer(本也考虑ijk,写着发现个弊端,倍速功能不支持6.0以下手机)
- 背景图和显示图片使用了Glide加载图片(包括高斯模糊效果)
- 旋转动画(重点是停止后继续播放当时确实头疼不好找)
简单介绍下开发背景(原因):
最近项目的第一个版本完成,第二个版本的迭代已经开始,产品经理剑走偏锋要求项目增加音频播放功能,包括音频的音量和倍速功能,优于之前并未做过该项功能,对于音频播放咱也是一知半解,于是乎我就开始对一些成功的开源项目进行了简单的研究,绝大多数都是基于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.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/my_music_showpopup"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#0000"/>
<ImageView
android: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" />
<ImageView
android: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"/>
<ImageView
android: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" />
<SeekBar
android: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" />
<LinearLayout
android: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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="音频加载失败"
android:textColor="#fff"
android:textSize="14sp" />
<TextView
android: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>
<ImageView
android: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" />
<ImageView
android: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"/>
<TextView
android: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" />
<TextView
android: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;