Android Studio如何实现音乐播放器(简单易上手)

2023-05-16

我们大家平时长时间打代码的时候肯定会感到疲惫和乏味,这个时候一边播放自己喜欢的音乐,一边继续打代码,心情自然也愉快很多。音乐带给人的听觉享受是无可比拟的,动听的音乐可以愉悦人的身心,让人更加积极地去热爱生活。大家平常应该会用QQ音乐、网易云音乐或者酷狗音乐等音乐APP来听歌,想不想拥有属于自己的音乐播放器。那么接下来就教大家如何用Android Studio自己制作一个音乐播放器APP。



项目目录

  • 一、项目概述
    • 1、需求分析
    • 2、设计分析
    • 3、资源文件分析
  • 二、开发环境
  • 三、准备工具
  • 四、详细设计
    • 1、新建工程
    • 2、搭建主界面
    • 3、创建服务类
      • 3.1、服务概述
      • 3.2、服务的创建
      • 3.3、服务的启动方式
      • 3.4、服务的生命周期
    • 4、搭建音乐播放界面
    • 5、搭建音乐列表界面
    • 6、搭建专辑界面
    • 7、导入资源文件
  • 五、项目运行
    • 1、创建模拟器
    • 2、运行演示
  • 六、项目总结
  • 七、源码下载
  • 八、升级优化

一、项目概述

1、需求分析

综合运用UI界面设计、数据存储、Activity(活动)、Service(服务)、MusicPlayer、ListView等知识,设计开发一款具有音乐列表的音乐播放器。

在这里插入图片描述

2、设计分析

整个项目包含五个类和五个布局文件:

  • 其中frag1、frag2为java文件,
  • MusicActivity为Activity文件,
  • MusicService为Service文件,
  • MainActivity为项目创建时自动生成的Activity文件。

在这里插入图片描述

  • activity_main为MainActivity的布局文件,显示运行APP时的主界面。
  • activity_music为MusicActivity的布局文件,显示音乐播放器界面。
  • music_list和item_layout一起组成frag1的布局文件,就是音乐列表界面。
  • frag2_layout就是frag2的布局文件,主要显示的是专辑封面图片。
    在这里插入图片描述

3、资源文件分析

在res文件夹下创建了raw文件夹,放了三首音乐文件(从音乐软件中下载即可),在drawable文件夹中粘贴了音乐封面图片bg.jpg和播放器背景图片music_bg.jpg,还有背景选择器的btn_bg_selector.xml文件,如图所示:

在这里插入图片描述

二、开发环境

我的开发环境如下,大家的AS版本不需要和我相同,只要是近两年从官网下载的版本,都是比3.6.1高的,是可以满足运行和开发要求的。

在这里插入图片描述

三、准备工具

  1. 选择几首自己下载好的音乐文件,命名为music0、music1、music2等,这里歌曲都要是mp3类型的,数量随意。歌曲名的下标是从0开始的,因为代码中我设置的url地址是从0开始的,否则会闪退。

  2. 选择每首歌对应的歌手图片,剪成圆形并且保存好,命名为music0、music1、music2等。图片一定要剪成圆形,椭圆会影响运行效果,导致闪退,如果不太清楚图片如何剪成圆形,可以看这篇博客:如何将图片剪成圆形,也非常简单。

  3. 准备一张音乐播放器的背景图片,命名为music_bg,再找一张图片用作专辑图片,命名为bg。图片的后缀是.png或.jpg。

四、详细设计

1、新建工程

  • 首先打开Android Studio,并新建一个工程,File——>New——>New Project——>Empty Project,工程名称叫做Music_List,可以根据自己喜好设置名称。

  • 包名自己随意设定,这里博主用的是zj.dzh,一般是com.android;工程文件的保存路径要修改一下,不要放在C盘,我这里选择的是放在E盘的一个Projects文件夹中,养成项目统一放在英文路径下的好习惯。

  • 最后选择API 18:Android 4.3,因为这样它就拥有了99.5%的跨平台性(兼容性非常好),因为它版本很低,基本上模拟器API版本都是高于20的,所以这个软件可以运行其他各种设备上。点击Finish完成创建。

在这里插入图片描述

2、搭建主界面

设计一个app的时候,一定要先设计layout文件,再设计java文件,因为布局有了,才能在上面进行代码的编写。我们来看一下activity_main布局文件,它主要显示主界面。什么是主界面,就是一个APP运行后默认显示的界面。

最外层用的是LinearLayout线性布局,然后设置的是垂直排列。里面放了两个小的LinearLayout,分别是最上面的一个TextView,内容设置的是“我喜欢”,接着第二个小的LinearLayout放两个水平的TextView控件,内容显示的是“歌曲”和“专辑”。

最下面的剩余空间全部给了Fragment,它的id是content,这里不知道Fragment没关系,反正就晓得它和TextView一样可以显示内容就行。效果如图:

Fragment是一种可以嵌入在活动中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间,出现的初衷是为了适应大屏幕的平板电脑,可以将其看成一个小型Activity,又称作Activity片段。

在这里插入图片描述

完整代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="我喜欢"
            android:textSize="35dp"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
    <TextView
        android:id="@+id/menu1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="歌曲"
        android:textSize="25dp"/>
    <TextView
        android:id="@+id/menu2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="专辑"
        android:textSize="25dp"/>
    </LinearLayout>
    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="9">
    </FrameLayout>

</LinearLayout>

然后回到我们的Activity文件。MainActivity类是整个工程的主类,首先创建需要用到的控件,然后绑定控件,再设置监听器,底部导航栏设置的是两个菜单文件:frag1(歌曲菜单)和frag2(专辑菜单),默认情况下展示frag1歌曲菜单,点击切换显示frag2专辑菜单。代码如下:

package zj.dzh.music_list;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    //创建需要用到的控件的变量
    private TextView tv1,tv2;
    private FragmentManager fm;
    private FragmentTransaction ft;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //绑定控件
            tv1=(TextView)findViewById(R.id.menu1);
            tv2=(TextView)findViewById(R.id.menu2);
            //设置监听器,固定写法
            tv1.setOnClickListener(this);
            tv2.setOnClickListener(this);
            //若是继承FragmentActivity,fm=getFragmentManger();
            fm=getSupportFragmentManager();
            //fm可以理解为Fragment显示的管理者,ft就是它的改变者
            ft=fm.beginTransaction();
            //默认情况下就显示frag1
            ft.replace(R.id.content,new frag1());
            //提交改变的内容
            ft.commit();
    }
    @Override
    //控件的点击事件
    public void onClick(View v){
        ft=fm.beginTransaction();
        //切换选项卡
        switch (v.getId()){
            case R.id.menu1:
                ft.replace(R.id.content,new frag1());
                break;
            case R.id.menu2:
                ft.replace(R.id.content,new frag2());
                break;
            default:
                break;
        }
        ft.commit();
    }
}

3、创建服务类

3.1、服务概述

在这里插入图片描述

3.2、服务的创建

在这里插入图片描述
创建MusicService文件,右击包名——>New——>Service——>Service,如图:

在这里插入图片描述

命名为MusicService,然后点击Finish。

在这里插入图片描述

3.3、服务的启动方式

在这里插入图片描述

3.4、服务的生命周期

在这里插入图片描述
MusicService代码:

package zj.dzh.music_list;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;

import java.util.Timer;
import java.util.TimerTask;
//这是一个Service服务类
public class MusicService extends Service {
    //声明一个MediaPlayer引用
    private MediaPlayer player;
    //声明一个计时器引用
    private Timer timer;
    //构造函数
    public MusicService() {}
    @Override
    public  IBinder onBind(Intent intent){
        return new MusicControl();
    }
    @Override
    public void onCreate(){
        super.onCreate();
        //创建音乐播放器对象
        player=new MediaPlayer();
    }
    //添加计时器用于设置音乐播放器中的播放进度条
    public void addTimer(){
        //如果timer不存在,也就是没有引用实例
        if(timer==null){
            //创建计时器对象
            timer=new Timer();
            TimerTask task=new TimerTask() {
                @Override
                public void run() {
                    if (player==null) return;
                    int duration=player.getDuration();//获取歌曲总时长
                    int currentPosition=player.getCurrentPosition();//获取播放进度
                    Message msg= MusicActivity.handler.obtainMessage();//创建消息对象
                    //将音乐的总时长和播放进度封装至bundle中
                    Bundle bundle=new Bundle();
                    bundle.putInt("duration",duration);
                    bundle.putInt("currentPosition",currentPosition);
                    //再将bundle封装到msg消息对象中
                    msg.setData(bundle);
                    //最后将消息发送到主线程的消息队列
                    MusicActivity.handler.sendMessage(msg);
                }
            };
            //开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒(0.5s)执行一次
            timer.schedule(task,5,500);
        }
    }
    //Binder是一种跨进程的通信方式
    class MusicControl extends Binder{
        public void play(int i){//String path
                Uri uri=Uri.parse("android.resource://"+getPackageName()+"/raw/"+"music"+i);
                try{
                    //重置音乐播放器
                    player.reset();
                    //加载多媒体文件
                    player=MediaPlayer.create(getApplicationContext(),uri);
                    player.start();//播放音乐
                    addTimer();//添加计时器
                }catch(Exception e){
                e.printStackTrace();
            }
        }
        //下面的暂停继续和退出方法全部调用的是MediaPlayer自带的方法
        public void pausePlay(){
            player.pause();//暂停播放音乐
        }
        public void continuePlay(){
            player.start();//继续播放音乐
        }
        public void seekTo(int progress){
            player.seekTo(progress);//设置音乐的播放位置
        }
    }
    //销毁多媒体播放器
    @Override
    public void onDestroy(){
        super.onDestroy();
        if(player==null) return;
        if(player.isPlaying()) player.stop();//停止播放音乐
        player.release();//释放占用的资源
        player=null;//将player置为空
    }
}

4、搭建音乐播放界面

在这里插入图片描述

右击包名,新建一个Activity,命名为MusicActivity,点击Finish,它会自动生成一个activity_music的布局文件,用来显示音乐播放界面,如图所示:

在这里插入图片描述

(1)MusicActivity类:通过onClick方法控制着音乐的播放、暂停、继续播放和退出功能。它和MusicService进行绑定连接。在音乐播放时显示歌曲总时长,还有歌曲当前播放时长,控制歌曲进度条的移动。代码如下:

package zj.dzh.music_list;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

import static java.lang.Integer.parseInt;

public class MusicActivity extends AppCompatActivity implements View.OnClickListener{
    //进度条
    private static SeekBar sb;
    private static TextView tv_progress,tv_total,name_song;
    //动画
    private ObjectAnimator animator;
    private MusicService.MusicControl musicControl;
    private String name;
    private Intent intent1,intent2;
    private MyServiceConn conn;
    //记录服务是否被解绑,默认没有
    private boolean isUnbind =false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_music);
        //获取从frag1传来的信息
        intent1=getIntent();
        init();
    }
    private void init(){
        //进度条上小绿点的位置,也就是当前已播放时间
        tv_progress=(TextView)findViewById(R.id.tv_progress);
        //进度条的总长度,就是总时间
        tv_total=(TextView)findViewById(R.id.tv_total);
        //进度条的控件
        sb=(SeekBar)findViewById(R.id.sb);
        //歌曲名显示的控件
        name_song=(TextView)findViewById(R.id.song_name);
        //绑定控件的同时设置点击事件监听器
        findViewById(R.id.btn_play).setOnClickListener(this);
        findViewById(R.id.btn_pause).setOnClickListener(this);
        findViewById(R.id.btn_continue_play).setOnClickListener(this);
        findViewById(R.id.btn_exit).setOnClickListener(this);

        name=intent1.getStringExtra("name");
        name_song.setText(name);
        //创建一个意图对象,是从当前的Activity跳转到Service
        intent2=new Intent(this,MusicService.class);
        conn=new MyServiceConn();//创建服务连接对象
        bindService(intent2,conn,BIND_AUTO_CREATE);//绑定服务
        //为滑动条添加事件监听,每个控件不同果然点击事件方法名都不同
        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            //这一行注解是保证API在KITKAT以上的模拟器才能顺利运行,也就是19以上
            @RequiresApi(api = Build.VERSION_CODES.KITKAT)
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                //进当滑动条到末端时,结束动画
                if (progress==seekBar.getMax()){
                    animator.pause();//停止播放动画
                }
            }

            @Override
            //滑动条开始滑动时调用
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
            @Override
            //滑动条停止滑动时调用
            public void onStopTrackingTouch(SeekBar seekBar) {
                //根据拖动的进度改变音乐播放进度
                int progress=seekBar.getProgress();//获取seekBar的进度
                musicControl.seekTo(progress);//改变播放进度
            }
        });
        //声明并绑定音乐播放器的iv_music控件
        ImageView iv_music=(ImageView)findViewById(R.id.iv_music);
        String position= intent1.getStringExtra("position");
        //praseInt()就是将字符串变成整数类型
        int i=parseInt(position);
        iv_music.setImageResource(frag1.icons[i]);
        //rotation和0f,360.0f就设置了动画是从0°旋转到360°
        animator=ObjectAnimator.ofFloat(iv_music,"rotation",0f,360.0f);
        animator.setDuration(10000);//动画旋转一周的时间为10秒
        animator.setInterpolator(new LinearInterpolator());//匀速
        animator.setRepeatCount(-1);//-1表示设置动画无限循环
    }
    //handler机制,可以理解为线程间的通信,我获取到一个信息,然后把这个信息告诉你,就这么简单
    public static Handler handler=new Handler(){//创建消息处理器对象
        //在主线程中处理从子线程发送过来的消息
        @Override
        public void handleMessage(Message msg){
            Bundle bundle=msg.getData();//获取从子线程发送过来的音乐播放进度
            //获取当前进度currentPosition和总时长duration
            int duration=bundle.getInt("duration");
            int currentPosition=bundle.getInt("currentPosition");
            //对进度条进行设置
            sb.setMax(duration);
            sb.setProgress(currentPosition);
            //歌曲是多少分钟多少秒钟
            int minute=duration/1000/60;
            int second=duration/1000%60;
            String strMinute=null;
            String strSecond=null;
            if(minute<10){//如果歌曲的时间中的分钟小于10
                strMinute="0"+minute;//在分钟的前面加一个0
            }else{
                strMinute=minute+"";
            }
            if (second<10){//如果歌曲中的秒钟小于10
                strSecond="0"+second;//在秒钟前面加一个0
            }else{
                strSecond=second+"";
            }
            //这里就显示了歌曲总时长
            tv_total.setText(strMinute+":"+strSecond);
            //歌曲当前播放时长
            minute=currentPosition/1000/60;
            second=currentPosition/1000%60;
            if(minute<10){//如果歌曲的时间中的分钟小于10
                strMinute="0"+minute;//在分钟的前面加一个0
            }else{
                strMinute=minute+" ";
            }
            if (second<10){//如果歌曲中的秒钟小于10
                strSecond="0"+second;//在秒钟前面加一个0
            }else{
                strSecond=second+" ";
            }
            //显示当前歌曲已经播放的时间
            tv_progress.setText(strMinute+":"+strSecond);
        }
    };
    //用于实现连接服务,比较模板化,不需要详细知道内容
    class MyServiceConn implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service){
            musicControl=(MusicService.MusicControl) service;
        }
        @Override
        public void onServiceDisconnected(ComponentName name){

        }
    }
    //判断服务是否被解绑
    private void unbind(boolean isUnbind){
        //如果解绑了
        if(!isUnbind){
            musicControl.pausePlay();//音乐暂停播放
            unbindService(conn);//解绑服务
        }
    }
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_play://播放按钮点击事件
                String position=intent1.getStringExtra("position");
                int i=parseInt(position);
                musicControl.play(i);
                animator.start();
                break;
            case R.id.btn_pause://暂停按钮点击事件
                musicControl.pausePlay();
                animator.pause();
                break;
            case R.id.btn_continue_play://继续播放按钮点击事件
                musicControl.continuePlay();
                animator.start();
                break;
            case R.id.btn_exit://退出按钮点击事件
                unbind(isUnbind);
                isUnbind=true;
                finish();
                break;
        }
    }
    @Override
    protected void onDestroy(){
        super.onDestroy();
        unbind(isUnbind);//解绑服务
    }
}

(2)activity_music: 显示音乐播放器界面,包括图片转动,歌曲名传值,还有播放、暂停播放、继续播放和退出四个控制按钮。效果如图:

在这里插入图片描述
activity_music代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/music_bg"
    tools:context=".Music_Activity"
    android:gravity="center"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/iv_music"
        android:layout_width="240dp"
        android:layout_height="240dp"
        android:layout_gravity="center_horizontal"
        android:layout_margin="15dp"
        android:src="@drawable/music0"/>
    <TextView
        android:id="@+id/song_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="歌曲名"
        android:textSize="20sp"/>
    <SeekBar
        android:id="@+id/sb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="8dp"
        android:paddingRight="8dp">
        <TextView
            android:id="@+id/tv_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00"/>
        <TextView
            android:id="@+id/tv_total"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="00:00"/>
    </RelativeLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/btn_play"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_margin="8dp"
            android:layout_weight="1"
            android:background="@drawable/btn_bg_selector"
            android:text="播放音乐"/>
        <Button
            android:id="@+id/btn_pause"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_margin="8dp"
            android:layout_weight="1"
            android:background="@drawable/btn_bg_selector"
            android:text="暂停播放"/>
        <Button
            android:id="@+id/btn_continue_play"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_margin="8dp"
            android:layout_weight="1"
            android:background="@drawable/btn_bg_selector"
            android:text="继续播放"/>
        <Button
            android:id="@+id/btn_exit"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_margin="8dp"
            android:layout_weight="1"
            android:background="@drawable/btn_bg_selector"
            android:text="退出"/>
    </LinearLayout>

</LinearLayout>

(3)右击drawable文件夹,New——>Drawable Resource File,命名为btn_bg_selector,如图:

在这里插入图片描述

该文件为按钮的背景选择器,是当播放等按钮按下的时候变成灰色,未按下显示白色,当然可以自由设定颜色。代码如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <corners android:radius="10dp"/>
            <solid android:color="#d4d4d4"/>
        </shape>
    </item>
    <item android:state_pressed="false">
        <shape android:shape="rectangle">
            <corners android:radius="10dp"/>
            <solid android:color="#ffffff"/>
        </shape>
    </item>
</selector>

5、搭建音乐列表界面

右击包名——>New——>Java Class,命名为frag1,点击OK。如图:

在这里插入图片描述

(1)frag1类:显示歌曲列表,手机模拟器运行出来的默认界面,就是将歌曲名、歌曲图片以列表的形式显示出来,注释很详细,代码如下:

package zj.dzh.music_list;

import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import androidx.fragment.app.Fragment;

public class frag1 extends Fragment {
    private View view;
    //创建歌曲的String数组和歌手图片的int数组
    public String[] name={"邓紫棋——光年之外","蔡健雅——红色高跟鞋","Taylor Swift——Love Story"};
    public static int[] icons={R.drawable.music0,R.drawable.music1,R.drawable.music2};
    @Override
    public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
        //绑定布局,只不过这里是用inflate()方法
        view=inflater.inflate(R.layout.music_list,null);
        //创建listView列表并且绑定控件
        ListView listView=view.findViewById(R.id.lv);
        //实例化一个适配器
        MyBaseAdapter adapter=new MyBaseAdapter();
        //列表设置适配器
        listView.setAdapter(adapter);
        //列表元素的点击监听器
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //创建Intent对象,参数就是从frag1跳转到MusicActivity
                Intent intent=new Intent(frag1.this.getContext(), MusicActivity.class);
                //将歌曲名和歌曲的下标存入Intent对象
                intent.putExtra("name",name[position]);
                intent.putExtra("position",String.valueOf(position));
                //开始跳转
                startActivity(intent);
            }
        });
        return view;
    }
    //这里是创建一个自定义适配器,可以作为模板
    class MyBaseAdapter extends BaseAdapter{
        @Override
        public int getCount(){return  name.length;}
        @Override
        public Object getItem(int i){return name[i];}
        @Override
        public long getItemId(int i){return i;}

        @Override
        public View getView(int i ,View convertView, ViewGroup parent) {
            //绑定好VIew,然后绑定控件
            View view=View.inflate(frag1.this.getContext(),R.layout.item_layout,null);
            TextView tv_name=view.findViewById(R.id.item_name);
            ImageView iv=view.findViewById(R.id.iv);
            //设置控件显示的内容,就是获取的歌曲名和歌手图片
            tv_name.setText(name[i]);
            iv.setImageResource(icons[i]);
            return view;
        }
    }

}

(2)右击layout文件夹——>New———>Layout Resource File,新建两个layout文件,分别命名为music_list(listview列表布局)和item_layout(子项布局),这两个布局文件对应着frag1逻辑代码,显示歌曲列表。如图所示:

在这里插入图片描述

music_list:歌曲列表布局文件,和QQ好友列表相似,垂直显示多条记录。效果如图:

在这里插入图片描述

music_list代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

item_layout: 就是条目布局,和上面的music_list列表一起组合使用,它就是相当于列表中每一行子项的具体布局,布局效果如图:

,

item_layout代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">
    <ImageView
        android:id="@+id/iv"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_centerVertical="true"/>
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/iv"
        android:layout_centerVertical="true">
        <TextView
            android:id="@+id/item_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="歌曲"
            android:textSize="15sp"
            android:textColor="#000000"/>
    </RelativeLayout>
</RelativeLayout>

6、搭建专辑界面

右击包名,New——>Java Class,命名为frag2,点击OK。如图:

在这里插入图片描述

(1)frag2类:展示的封面专辑图片,在主界面点击专辑按钮跳转到此界面。代码如下:

package zj.dzh.music_list;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.fragment.app.Fragment;

public class frag2 extends Fragment {
    //创建一个View
    private View zj;
    //显示布局
    public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        zj = inflater.inflate(R.layout.frag2_layout, null);
        return zj;
    }
}

(2)再新建一个layout文件,命名为frag2_layout,如图:

在这里插入图片描述

frag2_layout用来设置一个ImageView,显示专辑图片。当然frag2可以自由发挥,比如说设置成歌手资料、MV列表或者排行榜等等,比如音乐播放器2.0是歌手列表,多媒体播放器是MV列表。代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/zj"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg"/>

</LinearLayout>

7、导入资源文件

在res文件夹下新建一个raw文件夹,将刚刚准备好的音乐文件复制粘贴到raw文件夹中。对应的封面圆形图片、背景图片和专辑图片都复制粘贴到drawable中,这里博主只选择了三首歌曲和三张封面圆形图片,歌曲数量由大家自己设置,没有限制。

再次提醒下,注意命名哦;歌曲文件后缀是.mp3,在Android中打开是乱码不用担心,我们不需要打开它。

在这里插入图片描述

至此,完整的音乐播放器项目创建完成。

五、项目运行

1、创建模拟器

选择自己的模拟器运行,如果没有模拟器可以点击AVD,选择新建模拟器,选择手机型号Pixel 2:

在这里插入图片描述
选择高于21的API版本进行下载,下载成功之后next完成创建,如图:

在这里插入图片描述

2、运行演示

(1)运行app到模拟器上,显示主界面:

在这里插入图片描述
(2)点击专辑选项卡:

在这里插入图片描述

(3)再选择歌曲选项卡,选择第一首歌《光年之外》并打开,跳转到音乐播放页面,歌曲的封面图片显示在正中间,发现《光年之外》的歌名也传递过来了:

在这里插入图片描述

(4)点击播放音乐按钮,音乐开始播放,进度条开始滑动,图片开始转动:

在这里插入图片描述

(5)点击暂停播放按钮,音乐停止播放,而且进度条停止滑动,图片也停止转动:

在这里插入图片描述

(6)点击继续播放按钮,音乐继续播放,歌手图片也继续旋转:

在这里插入图片描述

(7)点击退出按钮,会退回到主界面:

在这里插入图片描述

(8)选择第二首歌《红色高跟鞋》并播放,这里进度条可以自由拖动,歌曲也会随之播放到对应位置:

在这里插入图片描述

(9)选择第三首歌《Love Story》并播放:

在这里插入图片描述

运行效果和功能很完整,至此就完成了非常简单的音乐播放器。大家可以跟着动手做一下,放上自己喜欢的爱豆图片,还有喜欢的歌,体验感真的不要太好!

六、项目总结

这次音乐播放器工程,可以让大家熟练掌握Android程序开发的基本技术,涉及Android 基础知识、UI界面、数据存储、四大组件、网络编程、高级编程等。大家熟练掌握可以对以后的Android开发有很大的帮助。



七、源码下载

需要源码学习的同学可以关注公众号《萌新加油站》,后台回复:音乐播放器,即可获取源码信息。
也可以直接下载源代码✨:Android Studio实现音乐播放器 🌟

博主从头到尾详细讲述了该项目的搭建运行步骤,代码里面也有详细的注释,如果对你有帮助的话,记得三连支持下哦。下方还有最新的音乐播放器2.0版本。

八、升级优化

今天是2021.7.10,我又对这个app进行了次更新,优化了播放器界面和上一首下一首功能,大家点击链接就可以直接跳转进行学习:👉🏻Android Studio实现音乐播放器【2.0版本】,喜欢的小伙伴多多三连支持哦!

🚀这有你错过的精彩内容🚀
Android Studio实现记事本
Android Studio实现选课系统
Android Studio实现购物商城
Android Studio实现图书管理系统
Android Stduio实现外卖订餐系统
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android Studio如何实现音乐播放器(简单易上手) 的相关文章

  • 树莓派自带VNC黑屏(灰屏)个人解决办法

    最近购买了树莓派 xff14 xff22 看来许多教程 xff0c 但VNC第一次还正常显示 xff0c 后来就黑屏 xff0c 还有什么现在不能显示桌面什么的 xff0c 下面给大家说下vnc黑屏我的解决办法 xff0c 我发现是vncv
  • Openmv色块追踪

    Openmv 拿到后第一次进行开发 xff08 颜色追踪 xff09 Openmv本身是具有很多的内置函数的 xff0c 基本都是别人给我们写好了 xff0c 需要我们自己去调用 xff0c 并给函数传递参数以实现我们需要的效果 但是很多初
  • XTDrone中roslaunch px4 indoor1.launch

    logging to home luyu ros log f8336262 666f 11ec ad21 4287fcc6d992 roslaunch luyu Lenovo 32405 log Checking log directory
  • Git的基本使用方法(0基础小白也能看懂)详细教程(含视频讲解)

    git指令介绍 xff0c 下面有详解指令可以先跳过直接看下面的详解 mkdir learngit 创建一个learngit文件夹 cd learngit 进入learngit文件夹 pwd 用于显示当前目录 git init 把这个目录变
  • 2020/05/25 Prometheus监控k8s企业级应用 1

    2 2 课程介绍及课程大纲 普罗米修斯的配置很难 2 3 Prometheus监控软件概述 prometheus是一名google的前员工写的 xff0c 也是go语言写的 xff0c K8S是第一个托管的项目 xff0c promethe
  • 现代信号处理——自适应滤波器(卡尔曼滤波)

    一 Kalman滤波的过程方程和观测方程 假设某系统n时刻的状态变量为x n 过程方程 xff1a x n 43 1 61 F n 43 1 xff0c n x n 43 v1 n 观测方程 xff1a y n 61 C n x n 43
  • 联想SR590服务器管理界面配置

    1 连接登录远程管理界面 将笔记本和服务器的管理口用网线连接 xff0c 配置笔记本的ip地址 xff0c 改成192 168 70 XXX xff0c 255 255 255 0 xff0c 如下图 xff1a 改完ip地址ping一下服
  • Qt鼠标点击不响应QMouseEvent事件的解决办法

    最近在写程序在和同事合完代码后发现一个问题 项目是使用qt开发一个程序 有一个功能是要实现地图的标记功能 我需要在一个label控件上显示地图 然后再选中标记按钮后点击地图 会在鼠标点击的位置放置一个标记的图标并储存备注信息 那么实现这个功
  • 匿名飞控程序整理

    主程序框图如下 xff0c 接下来我会将各个模块全部单独整理出来 xff0c 一是分享 xff0c 也是给自己留个便于查阅 主函数main c int main void flag start ok 61 All Init 进行所有设备的初
  • ORB_SLAM2 运行TUM RGBD数据集过程记录

    一 编译代码 这里是用ubuntu20 04 opencv4安装orbslam2的一点记录 报错信息经过搜索找到了对应的解决办法 xff0c 在这里做一个笔记 报错1 xff1a FATAL ERROR 34 OpenCV gt 2 4 3
  • 匿名飞控设备初始化All_Init()函数代码整理

    目录 初始化All Init 1 Para Data Init 参数初始化 2 Remote Control Init 遥控器输入类型 3 PWM Out Init 电调输出的初始化 4 spi 2初始化 xff0c 用于读取飞控板上所有传
  • 匿名飞控线程初始化和调度是怎么实现的

    目录 一 线程初始化 先看下Scheduler Setup xff08 xff09 函数 xff0c 二 任务调度器 函数在文件Ano Scheduler c中 xff0c Scheduler Setup xff08 xff09 初始化后
  • QGC编译报错error C2220: 警告被视为错误 最佳解决方法

    前面搜了一下 xff0c 试过各种方法依然报错 xff0c 先整理别人的解决方法 目录 一 重新保存文件 二 xff0c 更改编码方式 三 最终解决方案 xff1a 修改配置文件 一 重新保存文件 注意这里的配置 Wx WX 二 xff0c
  • 整理px4飞控校准机制和qgc校准机制

    用qgc执行 px4飞控传感器校准时 的步骤确实很麻烦 xff0c 磁罗盘需要转6个面 xff0c 加速度计也需要6个面 大疆无人机的校准很简单 xff0c 加速度计需要静置一会 xff0c 磁罗盘校准也就转一个面就行了 xff0c 就算学
  • mavros操作飞机时方向位置改为机体坐标系下指令

    前面试了很多 xff0c 看官网里的说明 用 mavros setpoint raw local 34 里的frame id改为 34 base link 34 不行 又直接发mavros msgs PositionTarget 修改里面的
  • px4室内飞行通过SLAM发送位置消息

    先按照官方的教程设置好飞控参数 http docs px4 io master zh computer vision visual inertial odometry html 下载源码学习 xff1a https github com A
  • MCUXpresso调试FreeRTOS时显示多线程

    多线程程序调试起来确实麻烦 xff0c 在调试过程中默认只能看到当前线程 按照以下配置即可在程序暂停时查看多线程的状态 xff1a 在debug配置中选中下图所示 xff1a 配置完成后再调试程序即可显示多线程
  • px4 编译生成msg消息机制及将其移植方法

    目录 一 在px4中添加新的Topic 二 px4生成msg生成的结构体优势 三 msg生成工具及移植方法 一 在px4中添加新的Topic 在px4中是通过uorb进行消息的传递 xff0c 通过根目录下的msg文件可以添加自定义的消息
  • px4传感器数据sensor模块整理

    涉及传感器数据的流程 xff0c 大致整理如下图 传感器的数据从drivers中调用lib中dirvers对应的对象更新数据并发布出去 xff0c sensor模块通过订阅原始数据和校准参数 xff0c 对数据滤波并更正 xff0c 然后再
  • C#实现程序一次打开两个窗口,两个窗口分别放置在两个屏幕

    这是本人在编程中碰到的一个问题 xff0c 寻找了其他案例 xff0c 都只是同时打开两窗口 xff0c 但不能实现在拥有两块显示屏时候 xff0c 分别将两个不同的窗口显示在两个单独的屏幕 源代码https download csdn n

随机推荐

  • 探寻C#事件本质

    我最先在学习C 事件的时候 xff0c 阅读了许多书籍 xff0c 但总是不能对事件建立起一个比较清晰的概念 xff0c 对其内部机制和原理也是似是而非 xff0c 因为这些书籍在描述事件的时候总是夹杂许多其他不能理解的抽象术语 xff0c
  • C#chart绘折线图动态添加数据

    C 入门基础 xff0c 实现chart控件动态添加数据 源程序https download csdn net download qq 42237381 10742048 目录 一 效果示意 二 原理说明 三 代码参考 一 效果示意 最终程
  • C#Winform程序制作仿真地平仪,磁罗盘飞行仪表盘

    因为项目需要做一个电子飞行仪表盘包括地平仪和磁罗盘 xff0c 网上找了很久 xff0c 没有找到一个合适的控件 xff0c 就学习了一些图形处理的开源代码做一个简陋的仪表盘来使用 xff0c 希望对其他人有帮助 xff0c 如果有好的建议
  • pixhawk学习

    原文转载至https blog csdn net u013181595 article details 80976610 1硬件架构分析 Pixhawk是一款基于ARM芯片的32位开源飞控 xff0c 由ETH的computer visio
  • 由浅入深对卡尔曼滤波的学习

    原文来自http shequ dimianzhan com articles 337 extended kalman filter course from shallow to deep 搬运过来好好学习 本篇译文翻译自 The Exten
  • px4中mixer_multirotor.cpp混控程序解读

    目录 一 函数说明 二 具体步骤 1 简单混控无边界 无边界输出 不混合偏航 2 限幅 3 加入比例缩放和偏航 使输出范围限制在 0 1 4 融合怠速处理 一 函数说明 混控函数在MIX函数 MultirotorMixer mix floa
  • pixhawk飞控板基于px4程序中串口调试打印数据的方法

    px4程序模块化非常的清晰 但程序也是非常的庞大 不同于一般程序员的编程惯例 所谓程序入参出参直白明显 px4的程序大多采用全局变量 而且使用UORB的通讯 使程序调用非常方便 但是使程序的可读性降低了很多 没有花一定的时间去研读很难看懂程
  • 安装win10和Ubuntu双系统开机启动项问题总结

    补充一下 xff0c 前面这是几年前刚开始用双系统的写的博客 xff0c 经验少 xff0c 但也适用于新电脑安装快 xff0c 在老电脑上装ubuntu不用这么麻烦了直接看后面的 目录 新电脑 xff08 系统没啥东西 xff09 自动分
  • pixhawk飞控板添加额外pwm输出通道控制开伞或舵机转动实现功能

    学习大佬的文章 xff0c 贴图来用 https blog csdn net dai93 article details 89852241 这是输出流的大致示意 如果在飞控需要控制单独的舵机 xff0c 可以自定义一个pwm通道输出 xff
  • px4 实现发送自定义mavlink协议消息解锁飞控板以及切换飞行模式

    原理 xff1a 通过mavlink控制台可以执行命令 xff0c 在循环中调用控制台指令的函数就行了 查阅commander文件下 所有模式切换都在commder main函数 int commander main int argc ch
  • pixhawk配置垂直起降无人机

    我使用的版本里面没有找到 43 型尾座式垂直起降无人机 就先用x型无人机了 查看代码 发现在init d目录下id号为13003 待续
  • 关于def __init__(self)

    首先说明一下 xff0c def init self 是用于初始化类 但是我们实际程序阅读过程中会发现 xff0c 这部分经常有两种状况 1 def init self xff0c 不带参数 xff0c 此处借鉴另一位博主的程序做一下分析
  • 《机器学习》周志华 集成学习

    8 1个体集成 个体学习器可以由 C4 5决策树算法 BP神经网络等 来训练 个体学习器 同质 gt 叫基学习器 也叫弱学习器 指泛化性能略优于随机猜测的学习器 xff0c 如二分类问题上精度略高于50 的分类器 相应的算法叫做 基学习算法
  • 【Windows内核编程】Win10/Win11通过PspCidTable取得EProcess

    温馨提示 本文的内容均在Windows 11 Enterprise 22000 466 版本下测试 不同版本的部分内容可能存在差异 xff0c 但万变不离其中 Upadate 20220803 经测试 xff0c 本文内容目前向下兼容 正文
  • 自抗扰控制的入门学习(一)—— 前言

    自抗扰控制的入门学习 xff08 一 xff09 前言 与很多人一样 xff0c 接触这个算法之后心态也经历过 xff0c 从一开始的 不明觉厉 xff0c 中途的 不以为然 xff0c 到最后的 辩证看待 的演变过程 借用别人的话 xff
  • Qt中QMessageBox的用法---看这一篇就够了

    一 详细说明 QMessageBox类提供一个模态对话框 xff0c 用于通知用户或询问用户一个问题并接收答案 消息框显示一个主要文本 xff0c 用于向用户提示某个情况 显示一个信息文本 xff0c 用于进一步解释警报或向用户询问问题 显
  • CCM-SLAM代码阅读

    ClintNode cpp boost span class token operator span shared ptr span class token operator lt span cslam span class token o
  • 树莓派之无屏幕无路由器网线直连树莓派(二)

    经上篇博客安装好系统后 xff0c 先不要拔出SD卡 xff0c 因为树莓派默认不开启ssh xff0c 所以先打开SD卡在里面新建文件命名为 ssh xff08 一定要小写 xff09 xff0c 然后找到cmdline txt文件打开并
  • SQL Server无法连接到服务器的解决方法

    SQL Server连接失败如何解决 xff1f 楼主安装了两次数据库 xff0c 都是官网最新的2017 Developer版本的 SSMS安装的是最新的18 3 经历了各种失败之后成功连接数据库 下面总结几种实用的方法给大家 xff0c
  • Android Studio如何实现音乐播放器(简单易上手)

    我们大家平时长时间打代码的时候肯定会感到疲惫和乏味 xff0c 这个时候一边播放自己喜欢的音乐 xff0c 一边继续打代码 xff0c 心情自然也愉快很多 音乐带给人的听觉享受是无可比拟的 xff0c 动听的音乐可以愉悦人的身心 xff0c