Android小玩意儿-- 从头开发一个正经的MusicPlayer(一)

2023-12-09 21:30

本文主要是介绍Android小玩意儿-- 从头开发一个正经的MusicPlayer(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前从未接触过音乐播放器这块东西的开发。今天偶然想做一个自己的音乐播放器。算是练练手。既然要做,就要做一个正儿八经的App。很多网上的资料也是模模糊糊,不是很全,现在开始,自己摸索着尝试着一步一步的做一个看看。中途一定会有很多的Bug让人头痛,但是,有了这些除Bug的经验,以后才能走的更顺,学的也才会更多。现在废话少说,先来干活儿。

1·获取手机中的音乐资源

首先不做别的,先看看怎么获取手机中的音乐资源。没有资源,后面做出来的也都是空架子。
Android系统中,我们要获取音乐资源有一个很好的类供我们使用,就是MediaStore,用官方的解释这个类就是:
The Media provider contains meta data for all available media on both internal and external storage devices.
这句话说得明明白白,所有的可用的多媒体资源,包括多媒体数据库的所有信息,包括音频,视频和图像,不论是在手机设备的内部存储空间还是外部存储空间,你都可以用MediaStore来获取他!android把所有的多媒体数据库接口进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去掉用那些封装好的接口就可以进行数据库的操作了!
有个这个类,获取手机中有关音乐的数据就方便多了。

首先,要得到一个ContentResolver实例,ContentResolver可以这样获取,利用一个Activity或者Service的Context即可。如下所示:
ContentResolver mResolver = ctx.getContentResolver();
上面的那个ctx的就是一个context,Activity.this就是那个Context,这个Context就相当于一个上下文环境。得到这个Context后就可以调用getContentResolver接口获取ContentResolver实例了。
ContentResolver实例获得后,就可以进行各种查询,我们要获得的是音频数据,音频数据增删改查的方法,视频和图像和音频非常类似。
下面开始讲述怎么在这些表上进行增删改查。
查询,代码如下所示:
Cursor cursor = resolver.query(_uri, prjs, selections, selectArgs, order);

ContentResolver的query方法接受几个参数,参数意义如下:

Uri:这个Uri代表要查询的数据库名称加上表的名称。这个Uri一般都直接从MediaStore里取得,例如我要取所有歌的信息,就必须利用MediaStore.Audio.Media. EXTERNAL _CONTENT_URI这个Uri。专辑信息要利用MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI这个Uri来查询,其他查询也都类似。

Prjs:这个参数代表要从表中选择的列,用一个String数组来表示。

Selections:相当于SQL语句中的where子句,就是代表你的查询条件。

selectArgs:这个参数是说你的Selections里有?这个符号是,这里可以以实际值代替这个问号。如果Selections这个没有?的话,那么这个String数组可以为null。

Order:说明查询结果按什么来排序。

上面就是各个参数的意义,它返回的查询结果一个Cursor,这个Cursor就相当于数据库查询的中Result,用法和它差不多。

增加,代码如下:

   ContentValues values = new ContentValues();values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,0);resolver.insert(_uri, values);

  这个insert传递的参数只有两个,一个是Uri(同查询那个Uri),另一个是ContentValues。这个ContentValuses对应于数据库的一行数据,只要用put方法把每个列的设置好之后,直接利用insert方法去插入就好了。

  更新,代码如下:

    ContentResolver resolver = ctx.getContentResolver();Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;ContentValues values = new ContentValues();values.put(MediaStore.Audio.Media.DATE_MODIFIED, sid);resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,values, where, selectionArgs);

上面update方法和查询还有增加里的参数都很类似,这里就不再重复叙述了,大家也可直接参考google的文档,那里也写的很清楚。

  删除,代码如下:

    ContentResolver resolver = ctx.getContentResolver();

   resolver.delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,where, selectionArgs);

有了这个基础,那么就可以操纵数据库的音乐数据了。
MainAcitvity.java获取数据库数据部分的代码

    Cursor mMusicCursor =this.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null, null,null, MediaStore.Audio.AudioColumns.TITLE);int indexTitle = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE);int indexArtist = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST);int indexTotalTime = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION);int indexPath = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);for(mMusicCursor.moveToFirst(); !mMusicCursor.isAfterLast(); mMusicCursor.moveToNext()) {String strTitle = mMusicCursor.getString(indexTitle);String strArtist = mMusicCursor.getString(indexArtist);String strTotalTime = mMusicCursor.getString(indexTotalTime);String strPath = mMusicCursor.getString(indexPath);}

这样就从数据库中拿出来了每一音频文件的信息

2·把信息进行封装

信息取出来了下一步就是要封装到一个对象里,并把每一个对象装到一个容器类中,方便我们使用。
首先要思考的是:用什么样的类来封装数据? 这个由封装的对象来决定。我们要封装的是Music,它有歌曲名,歌手名,歌曲时间,歌曲的路径地址。OK.可以进行封装了。
Music.java
package com.zharma.data;

    public class Music {private String musicName;private String musicArtist;private String musicPath;private String musicDuration;public Music(String musicName, String musicArtist, String musicPath, String musicDuration) {this.musicName = musicName;this.musicArtist = musicArtist;this.musicPath = musicPath;this.musicDuration = musicDuration;}public String getMusicName() {return musicName;}public String getMusicArtist() {return musicArtist;}public String getMusicPath() {return musicPath;}public String getMusicDuration() {return musicDuration;}}

有了封装对象的类,下面就做一个容器专门来装这些Music的对象。
MusicList.java
package com.zharma.data;

    import java.util.ArrayList;public class MusicList {private static ArrayList<Music> musicArray = new ArrayList<Music>();private MusicList() {}public static ArrayList<Music> getMusicList() {return musicArray;}}

这里我们把封装Music对象的列表ArrayList做成static的静态成员变量,这样他就成了这个类的公用变量,在第一次使用的时候就被初始化,也就是说对于这个MusicList类的所有new出来的对象,这个musicArray 被共享并且只有一份。

有了这两个类,下面就可以把音乐数据封装起来了。
用MianActivity.java的一个类来封装这段代码。

       private void initMusicList() {musicArrayList = MusicList.getMusicList();if(musicArrayList.isEmpty()){Cursor mMusicCursor = this.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,MediaStore.Audio.AudioColumns.TITLE);int indexTitle = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE);int indexArtist = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST);int indexTotalTime = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION);int indexPath = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);for (mMusicCursor.moveToFirst(); !mMusicCursor.isAfterLast(); mMusicCursor.moveToNext()) { String strTitle = mMusicCursor.getString(indexTitle);String strArtist = mMusicCursor.getString(indexArtist);String strTotoalTime = mMusicCursor.getString(indexTotalTime);String strPath = mMusicCursor.getString(indexPath);if (strArtist.equals("<unknown>"))strArtist = "艺术家";Music music = new Music(strTitle, strArtist, strPath, strTotoalTime);musicArrayList.add(music);}}}

到这里,就封装好了。数据库的音乐数据已经装进了我们的容器里。
接下来要做的就是把容器里的数据使用Adapter来逐一显示到Activity中。

3·使用Adapter连接后端数据和前端显示

先来介绍一下Adapter
Adapter
Adapter是连接后端数据和前端显示的适配器接口,是数据和UI(View)之间一个重要的纽带。在常见的View(ListView,GridView)等地方都需要用到Adapter。如下图直观的表达了Data、Adapter、View三者的关系:
121815506135900.jpg

我们的数据就是靠Adapter来显示到UI上的。
下面用SimpleAdapter来显示数据。
这段代码也封装在MainActivity.java的一个方法体里。

    private void initListView() {SimpleAdapter simpleAdapter;//构造一个列表来装Map对象,用map将music对象封装成键-值对List<Map<String, String>> list_map = new ArrayList<Map<String, String>>();HashMap<String, String> map;//for(Music music : musicArrayList) {map = new HashMap<String, String>();map.put("musicName", music.getMusicName());map.put("musicArtist", music.getMusicArtist());list_map.add(map);}String[] from = new String[] {"musicName", "musicArtist"};int[] to = { R.id.listview_tv_title_item, R.id.listview_tv_artist_item };simpleAdapter = new SimpleAdapter(this, list_map, R.layout.listview,from, to);listView.setAdapter(simpleAdapter);}

关于SimpleAdapter的构造函数的描述:
public SimpleAdapter (Context context, List<? extends Map<String, ?>>** data, int resource, String[] from, int[] to**)

Added in API level 1
Constructor

Parameters
context The context where the View associated with this SimpleAdapter is running
data A List of Maps. Each entry in the List corresponds to one row in the list. The Maps contain the data for each row, and should include all the entries specified in "from"
resource Resource identifier of a view layout that defines the views for this list item. The layout file should include at least those named views defined in "to"
from A list of column names that will be added to the Map associated with each item.
to The views that should display column in the "from" parameter. These should all be TextViews. The first N views in this list are given the values of the first N columns in the from parameter.

listview.xml布局文件:

       <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content" android:orientation="vertical"><TextView android:id="@+id/listview_tv_title_item"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="5dp"android:textSize="18sp"/><TextView android:id="@+id/listview_tv_artist_item"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:layout_marginBottom="5dp"android:textSize="16sp"/></LinearLayout>

activity_main.xml布局文件:
<?xml version="1.0" encoding="utf-8"?>

         <LinearLayoutandroid:id="@+id/main_volumeLayout"android:layout_width="fill_parent"android:layout_height="wrap_content"><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_weight="1"android:orientation="horizontal" ></LinearLayout><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:orientation="horizontal" ><TextViewandroid:id="@+id/main_tv_volumeText"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_weight="1"android:text="音量 :100%"android:textColor="#ffffffff"android:textSize="15dp" /><SeekBarandroid:id="@+id/main_sb_volumebar"android:layout_width="82dp"android:layout_height="wrap_content"android:maxHeight="5dip"android:minHeight="5dip"android:progressDrawable="@drawable/seekbar_style"android:thumb="@drawable/seekbar_thumb" /></LinearLayout></LinearLayout><ListViewandroid:id="@+id/main_listview"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_above="@+id/linearLayout1"android:layout_below="@id/main_volumeLayout"android:fastScrollEnabled="true"android:layout_marginLeft="10dip"android:layout_marginRight="10dip"android:background="@drawable/widget_bg"android:cacheColorHint="#00000000" /><LinearLayoutandroid:id="@+id/linearLayout1"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_marginBottom="10dip"android:layout_marginLeft="10dip"android:layout_marginRight="10dip"android:background="@drawable/widget_bg"android:orientation="vertical" ><LinearLayoutandroid:id="@+id/linearLayout2"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center" ><ImageButtonandroid:id="@+id/main_ibtn_pre"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="10dip"android:background="@drawable/button_previous" /><ImageButtonandroid:id="@+id/main_ibtn_play"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="10dip"android:background="@drawable/button_play" /><ImageButtonandroid:id="@+id/main_ibtn_stop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="10dip"android:background="@drawable/button_stop" /><ImageButtonandroid:id="@+id/main_ibtn_next"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="10dip"android:background="@drawable/button_next" /></LinearLayout><SeekBarandroid:id="@+id/main_seekBar"android:layout_width="fill_parent"android:layout_height="wrap_content"android:paddingLeft="10dip"android:paddingRight="10dip" /><RelativeLayoutandroid:id="@+id/relativeLayout2"android:layout_width="fill_parent"android:layout_height="wrap_content" ><TextViewandroid:id="@+id/main_tv_curtime"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:text="00:00" /><ImageView android:layout_toRightOf="@+id/main_tv_curtime"android:layout_marginLeft="5dp"android:id="@+id/main_iv_sleep"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/sleep_timer"android:background="@drawable/sleep_timer"/><TextViewandroid:id="@+id/main_tv_totaltime"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:text="00:00" /></RelativeLayout></LinearLayout></RelativeLayout>

在MianActivity.java中的代码如下:
package com.zharma.greatlovemusic;

    import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Timer;import com.zharma.data.Music;import com.zharma.data.MusicList;import android.support.v7.app.ActionBarActivity;import android.database.Cursor;import android.os.Bundle;import android.provider.MediaStore;import android.view.Menu;import android.view.MenuItem;import android.widget.ImageButton;import android.widget.ImageView;import android.widget.ListView;import android.widget.RelativeLayout;import android.widget.SeekBar;import android.widget.SimpleAdapter;import android.widget.TextView;public class MainActivity extends ActionBarActivity {// 显示组件private ListView listView;private RelativeLayout root_Layout;//歌曲列表对象private ArrayList<Music> musicArrayList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViews();initMusicList();initListView();}void findViews() {listView = (ListView) findViewById(R.id.main_listview);root_Layout = (RelativeLayout) findViewById(R.id.relativeLayout1);}/**初始化音乐列表对象*/private void initMusicList() {musicArrayList = MusicList.getMusicList();//避免重复添加音乐if(musicArrayList.isEmpty()){Cursor mMusicCursor = this.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,MediaStore.Audio.AudioColumns.TITLE);int indexTitle = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE);int indexArtist = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST);int indexTotalTime = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION);int indexPath = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);/**通过mMusicCursor游标遍历数据库,并将Music类对象加载带ArrayList中*/for (mMusicCursor.moveToFirst(); !mMusicCursor.isAfterLast(); mMusicCursor.moveToNext()) { String strTitle = mMusicCursor.getString(indexTitle);String strArtist = mMusicCursor.getString(indexArtist);String strTotoalTime = mMusicCursor.getString(indexTotalTime);String strPath = mMusicCursor.getString(indexPath);if (strArtist.equals("<unknown>"))strArtist = "艺术家";Music music = new Music(strTitle, strArtist, strPath, strTotoalTime);musicArrayList.add(music);}}}/**设置适配器并初始化listView*/private void initListView() {List<Map<String, String>> list_map = new ArrayList<Map<String, String>>();HashMap<String, String> map;SimpleAdapter simpleAdapter;for (Music music : musicArrayList) {map = new HashMap<String, String>();map.put("musicName", music.getMusicName());map.put("musicArtist", music.getMusicArtist());list_map.add(map);} String[] from = new String[] { "musicName", "musicArtist" };int[] to = { R.id.listview_tv_title_item, R.id.listview_tv_artist_item };simpleAdapter = new SimpleAdapter(this, list_map, R.layout.listview,from, to);listView.setAdapter(simpleAdapter);}}

转载于:https://www.cnblogs.com/zharma/p/4571338.html

这篇关于Android小玩意儿-- 从头开发一个正经的MusicPlayer(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/475123

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧