Android Studio实现音乐播放器2.0

2023-05-16

项目目录

  • 一、引言
  • 二、项目概述
    • 1、需求分析
    • 2、设计分析
    • 3、资源文件分析
  • 三、开发环境
  • 四、优化设计
    • 🌷1、上一首下一首
    • 🌾2、控件UI优化
    • 🍁3、登录和注册
    • 🌴4、数据库设计
    • 🌻5、歌手导航栏
  • 五、运行效果
  • 六、项目总结
  • 七、源码获取

一、引言

博主在一年前发过一篇用Android实现音乐播放器的教程:Android Studio如何实现音乐播放器(简单易上手)。当时实现的功能也很简单,就是播放音乐、暂停音乐、继续播放、退出播放、显示音乐列表和专辑封面的功能。如下图所示:

在这里插入图片描述

期间有很多同学问道我是否可以加上一首下一首功能,确实不难添加。但是当时因为写完这个最初版本后就没有当时那种心境再改了,我想大家都会有这种体会。

前段时间很忙,不过还是抽了些时间来回顾了自己的音乐播放器项目。也发现了很多不足的地方,然后花了几天时间进行了思考和优化升级,主要有六点:

  1. 增加了上一首、下一首功能
  2. 增加了数据库,记录用户账号和密码
  3. 增加了注册和登录界面,以及密码校验功能
  4. 增加了歌手导航栏,网格布局显示歌手的详细信息
  5. 优化了UI设计,包括Button、EditText和TextView的样式
  6. 优化了代码样式和项目结构,增加了大量的注释

所以,这篇博客就是对最初版本(1.0版本)的一个完善,即2.0版本!话不多说,先上图展示下:

歌曲导航栏

在这里插入图片描述

歌手导航栏

在这里插入图片描述

二、项目概述

1、需求分析

综合运用UI界面设计、Sqlite数据存储、Activity(活动)、Service(服务)、MusicPlayer(音乐播放类)、ListView(列表)、GridView(网格列表)、Fragment(碎片)、BaseAdapter(适配器)等知识,设计开发一款具有音乐列表的音乐播放器。

在这里插入图片描述

2、设计分析

下面是1.0的所有代码文件,包含五个java文件和五个layout文件,因为是比较简单的项目,代码之间的关系也标注出来了。
在这里插入图片描述
这是2.0的项目结构,使用不同的包来存放不同功能的代码文件,和1.0对比起来结构十分清晰。
在这里插入图片描述

3、资源文件分析

本项目的所有音乐文件都是存放在本地的,没有用服务器,当然也可以用,参考我的多媒体播放器:Android Studio实现多媒体播放器。

音乐文件存放在res文件夹下的raw文件夹中,图片资源存放在drawable和mipmap文件夹下,控件样式也是放在drawable中,颜色定义在values下的colors中。

下图左侧是drawable文件夹存放的播放器背景图片music_bg.jpg、歌手圆形图片music0.png、music1.png…,还有按钮图片play.png、pause.png等;右侧是raw文件夹下的歌曲文件music0.mp3、music1.mp3等。
在这里插入图片描述

三、开发环境

在这里插入图片描述

四、优化设计

🌷1、上一首下一首

想实现跳转到上一首和下一首歌曲的播放界面,肯定修改的是MusicActivity

1.1、可以看到onCreate方法里面intent1就是获取到的歌曲列表跳转到音乐播放界面的意图。我们换个巧妙的方法,利用这个intent1来实现。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //绑定布局文件
        setContentView(R.layout.activity_music);
        //获得意图
        intent1=getIntent();
        //初始化
        init();
    }

在frag1的列表点击事件里面,intent用键值对存储了歌曲名name[position]和下标position,分别存储到“name”和"position"这两个键中,然后传给MusicActivity。所以,这个position就是我们需要利用的。

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //创建Intent对象,启动音乐播放界面
                Intent intent=new Intent(frag1.this.getContext(), MusicActivity.class);
                //将数据存入Intent对象,利用键值对
                intent.putExtra("name",name[position]);
                intent.putExtra("position",String.valueOf(position));
                //开启意图
                startActivity(intent);
            }
        });

1.2、我们首先声明上一首、下一首这些按钮变量,然后绑定控件,设置监听器,修改下布局文件,这些大家肯定都很熟悉,所以这里也不再赘述了。

声明变量是在onCreate()的上面

    private Button play;//播放按钮
    private Button pause;//暂停按钮
    private Button con;//继续播放按钮
    private Button pre;//上一首按钮
    private Button next;//下一首按钮
    private Button exit;//退出按钮
    private ImageView iv_music;//歌手图片框

绑定以及设置监听器是在init( )中

		//依次绑定控件
        play=findViewById(R.id.btn_play);
        pause=findViewById(R.id.btn_pause);
        con=findViewById(R.id.btn_continue_play);
        pre=findViewById(R.id.btn_pre);
        next=findViewById(R.id.btn_next);
        exit=findViewById(R.id.btn_exit);

        //依次设置监听器
        play.setOnClickListener(this);
        pause.setOnClickListener(this);
        con.setOnClickListener(this);
        pre.setOnClickListener(this);
        next.setOnClickListener(this);
        exit.setOnClickListener(this);

完整的activity_music布局文件代码会在后面给出。

1.3、下面就是重写onClick()方法了,首先intent1是从歌曲列表界面跳转过来的意图。它传递过来歌曲下标position,因为用getStringExtra()方法获取的是字符串,所以用parseInt()转成整数i,这样就获取到这首歌的下标了。

       //定义歌曲列表传过来的下标position
        String position= intent1.getStringExtra("position");
        //将字符串转化为整型i
        int i=parseInt(position);

1.4、iv_music就是图片框,显示歌手的图片,name_song是歌曲名的文本框,显示歌曲名称。这里我在MusicActivity里面定义了一个musicName的数组,数组存放的就是歌曲名,和我们在frag1里面定义的name数组内容是一样的。其实这样我们就不需要刚刚的frag1传值给我们歌曲名了。
在这里插入图片描述

1.5、这时候我们来获取上一首歌,只需要i-1就可以了,但是问题来了,这样只能切换一次,继续下一首就会没有反应,因为onClick方法是每次被点击都要调用的。里面的position每次都初始化为那个i,所以你改变下标只能改变一次而已。

1.6、既然这样行不通,那么如何做到每次更新这个下标呢,这里可以自己定义个全局变量change,用来记录这个下标,每次加一或者减一,这个变量是全局变量,它的值是可以变化的,所以每次调用onClick()方法并不会复原值。

public int change=0;//记录下标的变化值

1.7、这样,不管是上一首i-1还是下一首i+1都可以直接再加上change就行了,代码如下:

case R.id.btn_pre://播放上一首
     if((i+change)<1) {
         change=musicName.length-1-i;
         musicControl.play(i+change);
         iv_music.setImageResource(frag1.icons[i+change]);
         name_song.setText(musicName[i+change]);
         pause.setVisibility(View.VISIBLE);
         animator.start();
         break;
     }
     else {
         change--;
         iv_music.setImageResource(frag1.icons[i+change]);
         name_song.setText(musicName[i+change]);
         musicControl.play(i+change);
         pause.setVisibility(View.VISIBLE);
         animator.start();
         break;
     }
case R.id.btn_next://播放下一首
     if((i+change)==musicName.length-1) {//这里musicName.length-1表示的最后一首歌的下标,即歌曲总数-1
         change=-i;
         musicControl.play(i+change);
         iv_music.setImageResource(frag1.icons[i+change]);
         name_song.setText(musicName[i+change]);
         pause.setVisibility(View.VISIBLE);
         animator.start();
         break;
     }
     else {
         change++;
         iv_music.setImageResource(frag1.icons[i+change]);
         name_song.setText(musicName[i+change]);
         musicControl.play(i+change);
         pause.setVisibility(View.VISIBLE);
         animator.start();
         break;
     }

1.8、这样在第一版基础上可以成功实现上一首下一首的效果:

在这里插入图片描述

🌾2、控件UI优化

2.1、音乐播放器按钮用自带的样式看起来对用户很不友好。所以博主又花了几个小时从各大图标网站找到了一款比较中意的UI图,这里推荐给大家一个超级好用的阿里巴巴矢量图标库。

在这里插入图片描述

2.2、通过WPS图片剪切,将它们裁剪成圆形图标,效果如下图所示:

在这里插入图片描述
2.3、然后修改layout文件,这里要将播放按钮、暂停按钮、继续播放按钮三个按钮进行重叠。我通过android:layout_centerHorizontal="true"来将它们三个全部水平居中,这样就重叠在一起了,但是重叠顺序也必须有讲究,最上层的是播放按钮(btn_play),第二层是暂停按钮(btn_pause),第三层是
继续播放按钮(btn_continue_play)。

同学甲:为什么最上层的是播放按钮?
博主:因为播放按钮点击之后要让它消失,而且用以后不再出现,所以它必须放第一个。
同学乙:为什么第二层的是暂停按钮?
博主:因为播放歌曲后肯定要能暂停歌曲,所以要能显示暂停按钮,而且暂停按钮点击之后会消失,显示最底下的继续播放按钮。
同学丙:那我按下继续播放按钮是不是歌曲继续播放,继续播放按钮消失,然后暂停按钮出现?
博主:你说的没错,就是这样的逻辑。

这里给出完整的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=".MusicActivity"
    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>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/btn_continue_play"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_centerHorizontal="true"
            android:background="@drawable/play"/>
        <Button
            android:id="@+id/btn_pause"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_centerHorizontal="true"
            android:background="@drawable/pause"/>
        <Button
            android:id="@+id/btn_play"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_centerHorizontal="true"
            android:background="@drawable/play" />
        <Button
            android:id="@+id/btn_pre"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="@drawable/pre"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:layout_toLeftOf="@id/btn_play"/>

        <Button
            android:id="@+id/btn_next"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="@drawable/next"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@id/btn_play"/>
        <Button
            android:id="@+id/btn_exit"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="@drawable/exit"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dp"
            android:layout_toRightOf="@id/btn_next"/>

    </RelativeLayout>
</LinearLayout>

还有很多控件样式都定义在drawable中,这里不再详细展示,细节还是比较多的。

在这里插入图片描述

🍁3、登录和注册

这部分内容就是模板类,博主在QQ页面跳转这篇中讲的很清楚,代码也是如法炮制。

应用一进去就是登录的activity,
然后点击注册按钮进入注册的activity,
然后注册完返回登录界面进行登录,
登录成功进入主界面。

🦄这是登录界面:

在这里插入图片描述

🐴这是注册界面:

在这里插入图片描述

🌴4、数据库设计

这是DataBaseHelper类的完整代码,使用是android自带的sqlite数据库。创建了music.db这个数据库,然后创建了user表,用来存放user的account以及password,也是非常标准的模板。数据库中的CRUD操作在UserDao中,这里不再详细讲述。

public class DataBaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE = "music.db";
    public static final int VERSION = 1;
    private Context mContext;

    //建表语句定义成字符串常量
    public static final String CREATE_USER = "create table user ("
            + "account text primary key,"
            + "password text)";

    //创建DB对象时的构造函数
    public DataBaseHelper(Context context) {
        super(context, DATABASE, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_USER);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

🌻5、歌手导航栏

在Bean包下先创建了Singer类,定义歌手属性,实现构造方法和get、set方法。然后在SingerUtils中定义了歌手属性的数组,是每位歌手的详细资料。

在这里插入图片描述

接着创建了SingerGridAdapter适配器,在每个子项中显示每位歌手的信息,使用ViewHodler复用item的空间,防止内存泄漏。最后在SingerPage的碎片中,为GridView设置适配器。

        //1、获得数据源,也就是所有对象的列表
        mDatas = SingerUtils.getSingerList();
        //2、适配器加载数据源
        adapter = new SingerGridAdapter(getContext(), mDatas);
        //3、为布局设置适配器
        gv.setAdapter(adapter);

歌手导航栏界面如下:

在这里插入图片描述

点击可以进入每位歌手的详情界面:

在这里插入图片描述

五、运行效果

Android Studio实现音乐播放器2.0

六、项目总结

本次项目主要是对最初版本的音乐播放器进行功能和UI上的优化,实现起来并不算复杂但是细节非常多,UI设计也很花时间。其实不管遇到什么问题,都应该耐心去分析问题,然后想出解决思路,最后解决问题。这个过程可能会很短暂,也可能会很漫长,但是我们要相信没有什么难题可以困住自己。

七、源码获取

点击右侧链接,即可下载源码👉:Android Studio实现音乐播放器2.0

✨还可以关注我的公众号《萌新加油站》,后台回复:音乐2.0,有很多优质的资料等你来学习。


🚀这有你错过的精彩内容
❤️Android Studio实现记事本❤️
❤️Android Stduio实现天气预报❤️
❤️Android Studio实现仓库管理系统❤️
❤️Android Studio实现校园二手交易系统❤️
❤️Android入门实战——做一个健康饮食搭配APP❤️

在你考虑是否读博前,先安稳读完硕士,然后看看自己是不是写文章的料,到那时候你就自然想清楚这个问题了,硕博连读然后毕业遥遥无期的很多很多,请先走好脚下的路。博士宽进严出,没有写paper的学术天赋就不要考虑了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android Studio实现音乐播放器2.0 的相关文章

  • @Autowired注入为null — 4种常见情况

    64 Autowired注入为null 情况一 使用过滤器 原因解决 情况二 没有添加注解 原因解决 情况三 xff08 没有被扫描到 xff09 原因解决 情况四 xff08 手动new xff09 原因解决 情况一 使用过滤器 原因 因
  • TDD项目实战-命令行参数解析

    认识1 基本规则2 三步骤3 任务分解法总结 项目1命令行参数解析01 任务分解法与整体工作流程1 API 构思与组件划分2 功能分解与任务列表3 红绿灯循环 02 识别坏味道与代码重构1 引入多态接口2 使用 抽象工厂 模式的变体来替换分
  • mapper扫描问题(Invalid bound statement (not found))

    分析 xff1a 通常来说这种情况是mybatis没有配置好 但是还有一种可能是你的mapperscan扫描问题 解决 xff1a 使用这个的时候应该扫描的是mapper层 如果我们用成全局的扫描 xff08 根目录 xff09 xff0c
  • 找不到org.springframework.cloud.client.loadbalancer.reactive.OnNoRibbonDefaultCondition

    原因 xff1a 该类存在于spring cloud commons jar 引用的jar包存在冲突 新版本的spring cloud commons中取消了OnNoRibbonDefaultCondition类 解决 xff1a 引入依赖
  • Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springfra..问题

    原因 xff1a 依赖冲突 解决 xff1a 例如我的是spring cloud starter gateway和spring boot starter web和spring boot starter webflux依赖冲突排除 sprin
  • 重构 - 消除重复的new创建

    如下 xff1a 有时会遇到这种重复的new创建 span class token keyword public span span class token keyword class span span class token class
  • IDEA快捷键-重构

    文章目录 重构项目案例参考重构技巧1 消除重复new创建重构技巧2 提炼函数 xff0c 消除重复计算 提炼函数提炼变量搬移函数inlIne使用 xff08 内联 xff09 inLine重构局部变量inLine重构方法 重构重构菜单栏ID
  • 重构 - 提炼函数,消除重复代码

    一 参考资料二 重构步骤 以提炼重复计算函数为例子演示代码具体步骤1 提取重复new创建2 提取会变化的信息3 使用抽取的共有信息 xff0c 并删除原有信息4 提取计算函数5 使用卫语句 xff0c 简化代码逻辑 一 参考资料 重构 2
  • 模板方法 + 工厂变体消除重复if else

    模板方法 43 工厂消除重复if else 1 将重复代码 xff0c 抽取到抽象类中2 子类实现抽象类3 使用工厂获取对象 思维导图 xff1a 示例代码 xff1a 1 将重复代码 xff0c 抽取到抽象类中 span class to
  • mapstruct学习及使用详解

    映射器定义基本映射自定义映射方法从多个源对象映射映射嵌套对象更新现有实例继承配置逆映射映射期间的异常处理 数据类型转换隐式类型转换映射集合映射策略映射流映射枚举定义默认值或常量定义默认表达式 映射器检索策略映射定制装饰器 64 Before
  • 开窗函数@sql学习

    参考链接 https zhuanlan zhihu com p 98655285 mysql8 0 43 开窗函数 开窗函数又称OLAP函数 xff08 Online Analytical Processing xff09 1 开窗函数的语
  • LWIP学习系列(一):OSI模型以及TCP/IP模型的整理

    一 OSI模型与TCP IP模型的对比图 这张图是从网上搜来的 xff0c 我认为能够比较好的对应其中的两种模型的差别 学习lwip对其中部分协议有个大致的了解就行了 xff0c 具体需要的时候 xff0c 在按需求进行学习
  • win10 ubuntu16 双系统共用蓝牙鼠标

    最近给新笔记本电脑装了win10 43 ubuntu16双系统 xff0c 发现原来在win10下已经配对的蓝牙鼠标 xff0c 在ubuntu下配对后 xff0c win10就不能用了 xff0c 需要重新配对才行 xff0c 反之亦然
  • Validation校检使用及验证反模式问题,详细

    Validation校检认识基本使用验证 Spring MVC 控制器的输入验证 service层输入验证持久化层 实体输入使用验证组为不同的用例验证不同的对象自定义验证错误自定义验证器以编程方式验证 验证反模式 使用问题 xff09 仅在
  • Springboot使用定时任务scheduler详解

    目录 认识示例代码定时任务 调度任务开启调度添加定时任务以固定延迟执行定时任务 fixedDelay以固定速率执行定时任务 fixedRate延迟第一次初始化 initialDelay以 ISO 时间格式指定间隔 fixedRateStri
  • @Builder导致的No primary or default constructor found 问题,原因分析和解决方法

    问题 span class token class name Resolved span span class token punctuation span span class token class name span class to
  • 使用stream流的map调用set()方法提示不存在变量R的实例,因此void符合R

    示例代码 span class token keyword public span span class token class name List span span class token generics span class tok
  • 使用gitee go将spring boot项目部署到云主机上并运行

    使用 进入Gitee Go xff0c 点击免费使用 选择要做流水线的仓库 xff0c 点击免费体验 进入界面后 xff0c 点击新建流水线 构建流水线 基本信息 流水线的名称 xff0c 当执行流水后会生成一个pipeline 20220
  • 目标追踪——光流法optical flow

    光流法简介 光流光流法光流的物理意义光流场 光流法基本原理金字塔方法基于光流的运动目标检测 xff08 前景检测 xff09 算法实现原理 光流 光流 xff08 optical flow xff09 是空间运动物体在观察成像平面上的像素运
  • Vim配置文件vimrc 2022_11_18

    Vimrc简介 vimrc是vim的配置文件 xff0c Vim编辑器相关的所有功能开关都可以通过 vimrc文件进行设置 备注 xff1a 文件名中的 rc 是出自 run commands 最初的源头是麻省理工学院在1965年发展的CT

随机推荐