Android自定义音乐播放器

一:首先介绍用了哪些Android的知识点:

1 MediaPlayer工具来播放音乐

2 Handle。因为存在定时任务(歌词切换,动画,歌词进度条变换等)需要由Handle来处理Ui相关内容

3 动态权限申请(该应用程序读取本地歌曲,并且设置音质相关属性)且这两个权限在Android6.0后都需要动态申请

4 手势控制 (左划和右划需要满足一定条件后可以进行切歌)

5 Service服务 (启动Service 绑定Service 前台Service)

6 BroadcastReceiver 广播,Service与Activity,Activity与Activity存在动态交互,需要广播实现

7 基本的重写View能力和Intent交互数据的能力

8 Animation动画 图片旋转 歌词更新

二:实现过程(简要步骤,下面会详细讲解)

1 先编写MusicInfo工具类。因为我们是从手机内存中去读取音乐的相关信息,那么读出的数据该存储到MusicInfo工具类集合中。

2 先申请权限,然后再去手机内存将音乐及其相关的信息读出来 ,用一个ListView容器去装载所有的本地音乐

PS:到了这里基本的音乐信息列表已经有了   这也是我们的主界面(音乐列表界面) 即: 展示音乐/歌曲列表;

3 这时候先不方去实现播放这类的功能,我们先去处理歌词

这里说明一下。一个歌词文件(.Lrc)里面内容格式如下

(张卫健--真英雄)

可以发现他由时间戳和歌词内容两部分组成。有了这个信息后。编写LrcContent工具类,用于记录歌词内容和歌词时间。然后去手机里面寻找歌曲对应的歌词文件,将其编码,读出,装载为LrcContent集合。

4 编写Service类,Service主要用来处理:音乐播放,前台服务。在播放状态改变的时候与播放音乐的Activity进行通信。该Service由主界面启动,后面的Activity绑定即可

5 编写播放音乐的Activity类(MusicPlay)。当我们从主界面(音乐列表界面)点击了一首具体的音乐时,就会调转到该Activity,所有首先,主界面Activity需要传递一些信息给该Activity。

(1) MusicInfo工具类集合。即手机中所有的音乐信息

(2)当前点击的歌曲,传递位置(position)即可

好,现在我们播放音乐的Activity有了所有的音乐信息,还有当前需要播放的歌曲位置。因为需要前台服务,所以我们把音乐播放的控制权交给Service,我们去绑定服务,然后把所有的音乐信息,还有当前需要播放的歌曲位置都传递给Serivce,Service来控制播放音乐。

6 好了,现在我们的程序可以播放音乐了,我们再来一步步完善细节,歌词同步,该功能自定义View实现,最后显现在播放音乐的Activity中。注意Mediaplayer有一个重要属性:

mediaPlayer.getCurrentPosition()。该方法会返回当前播放时间,不过返回的时候时毫秒(重)。

自定义View(LrcView 显示歌词),该View中除了传统的自定义View需要的OnDraw之类的方法外,还需要获取第三步中的LrcContent集合,有了这个我们就有了所有的歌词内容和歌词相应的时间,那么同步如何实现呢?音乐最终该View要显示在播放音乐的Activity(MusicPlay)中,我们去MusicPlay的布局文件申明该View,然后在MusicPlay中编写一个定时器,可以设定每一秒启动一次,定时器发送消息,在Handle中接受消息,处理消息。Handle中我们需要:获取歌曲当前播放时间,根据当前播放时间去LrcContent集合中寻求匹配的歌词。用invalidate()方法,通知自定义View重绘,来同步更新歌词

7 我们的音乐播放器还差一个重要的东西,音乐控制器部分。这部分需要来控制播放上一首,下一首,播放/暂停,音乐进度拖动,音量设置。

该部分不难,所以再这里不详细讲。

三:效果图  因为完整录制的GIF太大,传不上来所以分批处理

四:代码精讲 代码量也不很大,但是全贴出来挨着讲又影响阅读。所以部分节选和重要知识点一并讲解。

PS:源码中含有大量System.out.println("XXXX");语句。个人比较偏爱的一种测试方式。。应该不干扰阅读,忘见谅

1 权限获取,我们要做的第一件事就是去内存读取音乐相关信息,那么我们就需要获得相关的权限,从Android6.0开始部分权限不仅需要在AndroidManifest.xml文件中声明,还需要在运行程序的时候动态获取.这里以读取存储权限为例:

首先在AndroidManifest.xml中定义

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

在Java业务代码中先判断是否已经有权限,有权限就不再申请,没有就申请权限

//首先检查自身是否已经拥有相关权限,拥有则不再重复申请
int check = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) ;//没有相关权限
    if (check != PackageManager.PERMISSION_GRANTED){//申请权限  STORGE_REQUEST = 1
        ActivityCompat.requestPermissions(this , new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE} ,STORGE_REQUEST);}else {//已有权限的情况下可以直接初始化程序
    init();
}

当我们申请权限后,去判断用户是否给与了相关权限,如果赋予了就可以做我们的事情了

/*
申请权限处理结果
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode){case STORGE_REQUEST :if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//完成程序的初始化
                 init();System.out.println("程序申请权限成功,完成初始化") ;}else {System.out.println("程序没有获得相关权限,请处理");}break ;}}

关于权限这部分:有兴趣的可以看下参考下这篇博客

Android 6.0 运行时权限管理最佳实践

2 有了权限,我们就可以从手机中去读取音乐相关信息了,这里讲解下如何去读取信息

(1) 我们需要先创建一个存储音乐信息MusicInfo工具类

注意:代码没贴完,还有属性的get和set方法没列出

public class MusicInfo implements Serializable {private  int _id = - 1;       //音乐标识码private  int duration = -1 ;   //音乐时常private String artist = null ;    //音乐作者private String musicName= null ; //音乐名字private String album = null ;   //音乐文件专辑private String title = null ;  //音乐文件标题private int size  ;   //音乐文件的大小  返回byte大小private String data ;  //获取文件的完整路径private String album_id ; //实际存储为音乐专辑团片
}   

(2)读取音乐信息。

Android中的音乐信息存储再手机数据库中,那么我们就需要去访问数据库。这里介绍下如何访问数据库

首先要知道基本数据库查询操作,返回Cursor对象

public final Cursor query(Uri uri , //查询路径

String[] projection , //查询指定的列

String selection, //查询条件

String[] selectionArgs, //查询参数

String sortOrder } //查询结果的排序方式

那么就有了我们查询音乐的操作。

//申明ContentResolver对象,用于访问系统数据库
private ContentResolver contentResolver ;
//用于装载MusicInfo对象
private List<MusicInfo> musicInfos ;
//获取系统的ContentResolver
contentResolver = getContentResolver() ;//从数据库中获取指定列的信息
mCursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI ,new String[] {MediaStore.Audio.Media._ID ,MediaStore.Audio.Media.TITLE , MediaStore.Audio.Media.ALBUM ,MediaStore.Audio.Media.ARTIST , MediaStore.Audio.Media.DURATION ,MediaStore.Audio.Media.DISPLAY_NAME , MediaStore.Audio.Media.SIZE ,MediaStore.Audio.Media.DATA , MediaStore.Audio.Media.ALBUM_ID } , null ,null ,null) ;

我们已经查询出了所有的音乐信息,并且存储在mCursor对象中,现在我们需要把音乐信息装载到musicInfos工具类集合中。

但是注意,音乐专辑图片需要特殊处理:我们这是只查询出了音乐专辑id   MediaStore.Audio.Media.ALBUM_ID 我们需要根据音乐专辑id再去查询音乐专辑图片,用该图片来作为音乐图片。

/*
    获取本地音乐专辑的图片
 */
private String getAlbumArt(int album_id)
{String UriAlbum = "content://media/external/audio/albums" ;String projecttion[] =  new String[] {"album_art"} ;Cursor cursor = contentResolver.query(Uri.parse(UriAlbum + File.separator +Integer.toString(album_id)) ,projecttion , null , null , null);String album = null ;if (cursor.getCount() > 0 && cursor.getColumnCount() > 0){cursor.moveToNext() ;album = cursor.getString(0) ;}//关闭资源数据
    cursor.close();return album ;
}

这时还可能出现一个问题,可能存在本地专辑音乐图片不存在,所以我们要进行判断,没有专辑图片就使用默认的图片。好了,现在我们就可以装载音乐信息了

musicInfos = new ArrayList<>() ;
for (int i = 0 ; i < mCursor.getCount() ; i++)
{Map<String , String> map = new HashMap<>() ;MusicInfo musicInfo = new MusicInfo() ;//列表移动
    mCursor.moveToNext() ;//将数据装载到List<MusicInfo>中
    musicInfo.set_id(mCursor.getInt(0));musicInfo.setTitle(mCursor.getString(1));musicInfo.setAlbum(mCursor.getString(2));musicInfo.setArtist(mCursor.getString(3));musicInfo.setDuration(mCursor.getInt(4));musicInfo.setMusicName(mCursor.getString(5));musicInfo.setSize(mCursor.getInt(6));musicInfo.setData(mCursor.getString(7));//将数据装载到List<Map<String ,String>>中
    //获取本地音乐专辑图片
    String MusicImage = getAlbumArt(mCursor.getInt(8)) ;//判断本地专辑的图片是否为空
    if (MusicImage == null){//为空,用默认图片
        map.put("image" , String.valueOf(R.mipmap.timg)) ;musicInfo.setAlbum_id(String.valueOf(R.mipmap.timg));}else
    {//不为空,设定专辑图片为音乐显示的图片
        map.put("image" , MusicImage) ;musicInfo.setAlbum_id(MusicImage);}// musicInfo.setAlbum_id(mCursor.getInt(8));
    musicInfos.add(musicInfo) ;

3 MediaPlayer重要方法:MediaPlayer用来指定音乐文件和播放相关动作,所以我们需要掌握与音乐播放相关的方法。

方法: getCurrentPosition()

解释:返回 Int, 得到当前播放位置  毫秒单位时间

方法: getDuration()

解释:返回 Int,得到文件的时间

方法:isLooping()

解释:返回 boolean ,是否循环播放

方法:isPlaying()

解释:返回 boolean,是否正在播放

方法:pause()

解释:无返回值 ,暂停

方法:release()

解释:无返回值,释放 MediaPlayer 对象

方法:reset()

解释:无返回值,重置 MediaPlayer 对象

方法:seekTo(int msec)

解释:无返回值,指定播放的位置(以毫秒为单位的时间)

方法:setDataSource(String path)

解释:无返回值,设置多媒体数据来源【根据 路径】

方法:setLooping(boolean looping)

解释:无返回值,设置是否循环播放

方法:start()

解释:无返回值,开始播放

方法:stop()

解释:无返回值,停止播放

方法:setVolume(float leftVolume, float rightVolume)

解释:无返回值,设置音量

事件:setOnCompletionListener(MediaPlayer.OnCompletionListener listener)

解释:监听事件,网络流媒体播放结束监听

事件:setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener)

解释:监听事件,网络流媒体的缓冲监听

事件:setOnErrorListener(MediaPlayer.OnErrorListener listener) 
解释:监听事件,设置错误信息监听

4 歌词处理  说下思路把:首先根据歌曲去内存寻找对应的歌词文件,找到后解码为歌词内容和歌词时间,并且把歌词内容和个时间装载到歌词信息类集合中。然后提供一个外部访问的方法用于向外部输出歌词信息

// 注:LrcContent是歌词处理类 其中包含两个参数,歌词时间和歌词内容
public class LrcProcess {//所有需要处理的歌词对象private List<LrcContent> lrcList;//一个歌词对象private LrcContent mLrcContent;/*构造函数完成对象的初始化*/public LrcProcess() {lrcList = new ArrayList<>();}/**从内存中读取歌词文件,并转换为String对象输出*/public String readLrc(String path){//用StringBuild来存储歌词内容StringBuilder sb = new StringBuilder() ;//获取歌词文件  因为传入的文件为MusicInfo类中的Data内容,其文件为mp3,需要更换为lrc文件File f = new File(path.replace(".mp3" , ".lrc")) ;try{//通过文件流对象来获取文件内容并且导入歌词内容对象集合中(lrcList)FileInputStream inputStream = new FileInputStream(f) ;InputStreamReader streamReader = new InputStreamReader(inputStream , "utf-8") ;BufferedReader bufferedReader = new BufferedReader(streamReader) ;String tempStr = "" ;while((tempStr = bufferedReader.readLine()) != null){//实现字符替换tempStr = tempStr.replace("[" , "") ;tempStr = tempStr.replace("]" , "@") ;//根据@分号对文件分离String[] splitData = tempStr.split("@") ;//  System.out.println("THE TEMP STR IS " + splitData[0]) ;if (splitData.length > 1){//新建歌词内容对象mLrcContent = new LrcContent();//设置歌词文本内容mLrcContent.setLrcStr(splitData[1]);//设置歌词时间int lrcTime = timeToStr(splitData[0]) ;mLrcContent.setLrcTime(lrcTime);//添加到列表lrcList.add(mLrcContent);//  System.out.println("录入歌词成功") ;}else {// System.out.println("录入歌词失败") ;}}}catch (FileNotFoundException e){sb.append("木有歌词文件,赶紧去下载!...");} catch (UnsupportedEncodingException e) {e.printStackTrace();sb.append("木有读取到歌词哦!");} catch (IOException e) {e.printStackTrace();sb.append("木有读取到歌词哦!");}return sb.toString() ;}/** 对歌词文件lrc中的时间内容进行转码* [00:02.32]陈奕迅   时间分别代表分,秒,毫秒* [00:03.43]好久不见*/public int timeToStr(String timeStr){timeStr = timeStr.replace(":" , ".") ;timeStr = timeStr.replace("." , "@") ;String []splitTime = timeStr.split("@") ;//分离出分, 秒, 毫秒 int minute = Integer.parseInt(splitTime[0]) ;int second = Integer.parseInt(splitTime[1]) ;int millisSecond = Integer.parseInt(splitTime[2]) ;int time = (minute * 60 + second) * 1000 + millisSecond ;return time ;}/*提供一个外界方法歌词对象集合的方法*/public  List<LrcContent> getLrcList(){return lrcList ;}}
//实例化歌词处理对象   Activity中调用方法  //musicInfosList为封装好的所有音乐信息类,get(position)可以获取当前音乐信息,getData获取音乐路径
LrcProcess  mLrcProcess = new LrcProcess() ;
mLrcProcess.readLrc(musicInfosList.get(position).getData()) ;

这里再补一个内容:通常经常下载的歌曲都是没有歌词文件的,所以我们需要自己去下载歌词文件,注意该程序中你需要把歌词文件和音乐文件放在一个地方,并且歌词文件和音乐文件前缀相同只是将.mp3改为了.lrc。我用的是网易云音乐,这里顺便推荐一个网易云下载歌词的网址,大神们各种推荐供你选择

知乎:网易云音乐怎么下载歌词?

5 歌曲播放界面

看图分析:

(1)标题动画

这两个TextView的布局代码就不再列出了。动画效果主要是继承了Animation类自定实现。

public class TextAnimation extends Animation {private float currentX ;  //指定X
    private float currentY ;  //指定Y
    //定义持续时间
    private int duration ;//设定Camera
    private Camera camera = new Camera() ;public TextAnimation(float x , float y ,int duration){currentX = x ;currentY = y ;this.duration = duration ;System.out.println("THE X IS " + currentX + "\nTHE Y IS " + currentY) ;}@Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {super.initialize(width, height, parentWidth, parentHeight);//设置持续时间
        setDuration(duration);//设置动画结束后保留
        setFillAfter(true);//设置变换速度
        setInterpolator(new AccelerateDecelerateInterpolator());  //减速
        //setInterpolator(new AccelerateInterpolator());        //加速 默认情况
        //setInterpolator(new LinearInterpolator());          //匀速
    }@Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {super.applyTransformation(interpolatedTime, t);/*
        保存
         */
        camera.save();//根据interpolatedTime来控制X Y Z上的偏移
        camera.translate(10.0f - 10.0f * interpolatedTime ,30.0f  - 30.0f * interpolatedTime ,80.0f - 80.0f * interpolatedTime);//根据interpolatedTime在X轴做角度变换
        camera.rotateX(360 * interpolatedTime);//根据interpolatedTime在Y轴做角度变换
        camera.rotateY(360 * interpolatedTime);//获取Transformation参数封装的matrix对象
        camera.getMatrix(t.getMatrix());t.getMatrix().preTranslate(-currentX / 4 , -currentY / 4) ;t.getMatrix().postTranslate(currentX , currentY) ;/*
        如果存在保存的状态,就恢复
         */
        camera.restore();}}
initialize(int width, int height, int parentWidth, int parentHeight)中,width和height代表指定播放动画的View空间宽高,parentWidth和parentHeight代表该View控件所在的父控件宽高。可以在该方法中获取View的宽和高,但是我在这里面主要是完成的初始化

applyTransformation()方法是动画具体的实现方法,在系统绘制动画时会反复调用这个方法,每调用一次applyTransformation()方法,其中的interpolatedTime参数都会改变一次,值从0到1递增,当interpolatedTime的值为1时则动画结束。Transformatio类是一个变换的矩阵,通过改变该矩阵就可以实现各种复杂的效果。

PS:更多的相关的动画知识可以阅读官方文档,或者其它博文,书籍,网络视频获取。
Activity调用该动画方法:
/*
获取并且设置音乐的标题和歌手。并添加动画来显示
 */
MusicArtist = (TextView) findViewById(R.id.MusicArtist) ;
MusicName = (TextView) findViewById(R.id.MusicName) ;
MusicName.setText(musicInfosList.get(position).getTitle());
MusicArtist.setText(musicInfosList.get(position).getArtist());
//动画效果
MusicArtist.setAnimation(new TextAnimation(0 , 0 , 2000));
MusicName.setAnimation(new TextAnimation(0 , 0 , 2000));
(2)音乐专辑图片处理

1,圆形图片

需要用到Shader

Shader的使用步骤:
1. 构建Shader对象
2. 通过Paint的setShader方法设置渲染对象
3.设置渲染对象
4.绘制时使用这个Paint对象
public class XCRoundImageView extends android.support.v7.widget.AppCompatImageView{private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG);private Bitmap mRawBitmap;private BitmapShader mShader;private Matrix mMatrix = new Matrix();public XCRoundImageView(Context context, AttributeSet attrs) {super(context, attrs);}@Override
    protected void onDraw(Canvas canvas) {//获取原生Bitmap位图
        Bitmap rawBitmap = getBitmap(getDrawable());if (rawBitmap != null){//获取图片宽和高
            int viewWidth = getWidth();int viewHeight = getHeight();//由于要变换为圆形,在变换过程中取边长相对小的为基准
            int viewMinSize = Math.min(viewWidth, viewHeight);float dstWidth = viewMinSize;float dstHeight = viewMinSize;//如果是第一次绘制
            if (mShader == null || !rawBitmap.equals(mRawBitmap)){mRawBitmap = rawBitmap;/*
                BitmapShader是Shader的子类,可以通过Paint.setShader(Shader shader)进行设置、
                这里我们只关注BitmapShader,构造方法:
                mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
                参数1:bitmap
                参数2,参数3:TileMode;
                TileMode的取值有三种:
                CLAMP 拉伸
                REPEAT 重复
                MIRROR 镜像
                如果大家给电脑屏幕设置屏保的时候,如果图片太小,可以选择重复、拉伸、镜像;
                重复:就是横向、纵向不断重复这个bitmap
                镜像:横向不断翻转重复,纵向不断翻转重复;
                拉伸:这个和电脑屏保的模式应该有些不同,这个拉伸的是图片最后的那一个像素;横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复;
                */
                mShader = new BitmapShader(mRawBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);}if (mShader != null){/*
                 void setLocalMatrix(Matrix localM);
                 设置shader的本地矩阵,如果localM为空将重置shader的本地矩阵。
                 */
                mMatrix.setScale(dstWidth / rawBitmap.getWidth(), dstHeight / rawBitmap.getHeight());mShader.setLocalMatrix(mMatrix);}mPaintBitmap.setShader(mShader);float radius = viewMinSize / 2.0f;canvas.drawCircle(radius, radius, radius, mPaintBitmap);} else {super.onDraw(canvas);}}private Bitmap getBitmap(Drawable drawable){if (drawable instanceof BitmapDrawable){return ((BitmapDrawable)drawable).getBitmap();} else if (drawable instanceof ColorDrawable){Rect rect = drawable.getBounds();int width = rect.right - rect.left;int height = rect.bottom - rect.top;int color = ((ColorDrawable)drawable).getColor();Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));return bitmap;} else {return null;}}
}

PS:作者当时也是搬运的,只是补充了部分注释。内容可能有点复杂,需要多实践,建议参考资料文档

自定义控件之 圆形 / 圆角 ImageView

自定义控件三部曲之绘图篇(十八)——BitmapShader与望远镜效果

2,图片旋转

首先定义旋转动画 :image_rotate.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/linear_interpolator"><rotateandroid:fromDegrees="0"android:toDegrees="359"android:pivotX="50%"android:pivotY="50%"android:duration="8000"/>
</set>

然后在Java业务代码中设定定时器来启动动画

/*
该Timer用于实现:音乐播放界面图片旋转动画
 */
MyHandle2 handle2 = new MyHandle2() ;
new Timer().schedule(new TimerTask() {@Override
    public void run() {handle2.sendEmptyMessage(0x112) ;}
}, 0 ,8000);

最后再Handle中接收消息,并且启动动画

public class MyHandle2 extends  Handler
{@Override
    public void handleMessage(Message msg) {if ((msg.what == 0x112)){//设置图片旋转
            MusicImage.setAnimation(AnimationUtils.loadAnimation(MusicPlay.this , R.anim.image_rotate));}}
}

五:下载地址github

https://github.com/547291213/MusicPlayer

六:参考资料

  • Android音乐播放器(一):搜索手机存储的音乐
  • Android 6.0 运行时权限管理最佳实践
  • android 完美获取音乐文件中的专辑图片并显示
  • Android开发笔记(一百二十六)自定义音乐播放器
  • Android应用开发--MP3音乐播放器滚动歌词实现
  • Android实现音乐示波器、均衡器、重低音和音场功能
  • 自定义控件之 圆形 / 圆角 ImageView
  • 【Android】Service前台服务的使用
  • 官方开发文档

七:总结

第一次写这么长文的博客,也没想过邀功。不过真心希望您再阅读了这篇博文,并且有所建议和收获后留下您宝贵的评论。谢谢。

PS:转载请注明 https://blog.csdn.net/qq_29989087/article/details/80206290

Android 自定义音乐播放器实现相关推荐

  1. Android开发笔记(一百二十六)自定义音乐播放器

    MediaRecorder/MediaPlayer 在Android手机上面,音频的处理比视频还要复杂,这真是出人意料.在前面的博文< Android开发笔记(五十七)录像录音与播放>中, ...

  2. android 音乐播放器 获取sd卡所有音乐文件,Android Studio音乐播放器无法读取SD卡,只有内部存储器...

    我很抱歉,如果这原来是一个愚蠢的问题,它可能会成为一个快速修复,但我只是无法弄清楚.我在android studio中创建了音乐播放器,并且没有任何sdcard上的歌曲不会显示在列表视图中,只有内部内 ...

  3. android 自定义MP4播放器

    昨天,在网上找了好多资料,终于做了一个自定义的播发器. 视频播放方式 在Android中播放视频的方式有两种: 1.使用MediaPlayer结合SurfaceView进行播放.其中通过Surface ...

  4. 【Android】音乐播放器APP的设计与实现

    [Android]音乐播放器APP的设计与实现 一.界面设计 二.核心代码 一.界面设计 (1)注册登录 (2)主界面 (3)音乐播放器 可以实现开始,暂停,下一首,上一首功能:滑动进度条可以改变音乐 ...

  5. 三款Android平台音乐播放器多方位对比

    音乐播放器作为重要的娱乐工具,在PC端异常重要,在移动端同样如此.移动端的音乐播放器很多,巨头们纷纷在这个领域布局,腾讯百度等均拥有各自的移动端音乐播放器.爱好音乐的机友们也面临较多的选择. 小编在这 ...

  6. Vue 自定义音乐播放器组件为H5添加背景音乐

    自定义音乐播放器组件为H5添加背景音乐: 1.创建music.vue组件 <template><div><div @click="changeOn" ...

  7. android仿音乐播放器,Android仿音乐播放器功能

    本文实例为大家分享了Android仿音乐播放器功能的具体代码,供大家参考,具体内容如下 读取本地音乐文件 源代码: import android.media.MediaPlayer; import a ...

  8. android 小型音乐播放器(实现播放、下一首、上一首、自动播放、随机播放按钮、当前播放歌曲界面)

    该文章主要针对 android 的音乐播放器软件进行简单的功能添加:播放.断点播放.停止.上一曲.下一曲.随机播放.显示当前播放歌曲(后续会为当前显示的播放添加动画): 软件开发流程: 1.先向内存卡 ...

  9. Android在线音乐播放器

    波尼音乐 系列文章 Android开源在线音乐播放器--波尼音乐 Android开源音乐播放器之播放器基本功能 Android开源音乐播放器之高仿云音乐黑胶唱片 Android开源音乐播放器之自动滚动 ...

最新文章

  1. 操作系统期末复习重点题型归纳
  2. GraphPad Prism —— 简单又好用的生物数据统计绘图软件
  3. (九)javaScript的基本使用
  4. Permission denied: user=root, access=WRITE, inode=/:hadoopuser:supergroup:drwxr-xr-x
  5. Vue003_class 与style 绑定
  6. 【Hadoop】MapReduce笔记(二):MapReduce容错,任务失败处理
  7. 同步装饰器来替换线程安全类
  8. delphi android动态权限,DELPHI安卓动态权限申请
  9. qt实现窗口拖动的两种思路
  10. chrome postman插件_收集了一些Chrome插件神器,助你快速成为老司机
  11. c++11 常用语法
  12. 对象可以创建数组吗_电脑零基础可以学习创建网站吗?
  13. 不常用,但热门的IOS第三方库(转)
  14. 测量中的坐标与时间系统1.2(在大地测量学中)
  15. 大数据开发学习教程——大数据的基本概念
  16. Python人眼视线追踪系统
  17. 洛谷1852 BZOJ2144 跳跳棋 思维题
  18. 在上海相亲碰到如此势利贪婪的“80后”女孩
  19. 一笔画 java_NYOJ42 一笔画问题
  20. 如何从Matlab中导出清晰的结果图片

热门文章

  1. 网页设计之栅格系统及应用
  2. eb8000软件怎样上传_EB8000软件使用介绍幻灯片
  3. 辽宁省高考成绩查询时间2021,2021年辽宁高考成绩什么时候出来,今天几点钟出成绩可以查询...
  4. 虚拟光盘刻录光盘的大师级软件Alcohol 120%教程
  5. 《使用「Markdown」编辑器的那些天 |CSDN编辑器测评》
  6. JavaScript的内存作用域闭包
  7. CIO免费IT预算计划模板
  8. 硬件经典面试 100 题
  9. SparkShell操作
  10. 软件项目管理课程论文