之前从未接触过音乐播放器这块东西的开发。今天偶然想做一个自己的音乐播放器。算是练练手。既然要做,就要做一个正儿八经的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三者的关系:

我们的数据就是靠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>** 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布局文件:

	     <LinearLayout
android:id="@+id/main_volumeLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
> <LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal" >
</LinearLayout> <LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal" > <TextView
android: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" /> <SeekBar
android: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>
<ListView
android: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" /> <LinearLayout
android: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" > <LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center" > <ImageButton
android:id="@+id/main_ibtn_pre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:background="@drawable/button_previous" /> <ImageButton
android:id="@+id/main_ibtn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:background="@drawable/button_play" /> <ImageButton
android:id="@+id/main_ibtn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:background="@drawable/button_stop" /> <ImageButton
android: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> <SeekBar
android:id="@+id/main_seekBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:paddingRight="10dip" /> <RelativeLayout
android:id="@+id/relativeLayout2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" > <TextView
android: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"
/> <TextView
android: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; @Override
protected 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);
} }

Android小玩意儿-- 从头开发一个正经的MusicPlayer(一)的更多相关文章

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

    MusicService已经能够接收广播,通过广播接收的内容来做出相应的MediaPlayer对象的处理,包括播放,暂停,停止等,并当MediaPlayer对象的生命周期发生变化的时候,同样通过发送广 ...

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

    1·在Service中实例化MusicPlayer,实现对整个播放过程的控制 上一次做到了找到音乐数据,并封装成对象装在ArrayList里,把数据的信息显示在UI上.下面一个阶段就要开始真正的音乐播 ...

  3. 微信小程序实战--开发一个简单的快递单号查询

    功能如图: 虽然工作中只负责小程序后台开发,但是还是小程序开发产生了浓厚的兴趣,官方文档也是超级详细了 这里就简单做一个快递单号的查询: 新建一个page: 接着就可以写wxml了.这里用一个简单的i ...

  4. 用Visual Studio 2015 编写 MASM 汇编程序(二)从头开发一个Win32汇编程序

    一,建立一个VC的控制台类型的空工程: 1,从VS菜单中选择“文件”->“新建”->“项目”. 2,在新建项目中选择:“Visual c++”->"Win32"- ...

  5. Android | 教你如何用代码开发一个拍照翻译小程序

    引子   想必有很多小伙伴喜欢外出旅游,能去海外玩一圈那是更好不过了,旅游前大家一定会对吃.穿.住.行.游玩路线做各种攻略,然后满怀期待的出发- 想象中的旅游   出发前,想象中的旅游目的地可能有漂亮 ...

  6. Android | 教你如何用华为HMS MLKit 图像分割 SDK开发一个证件照DIY小程序

    Android | 教你如何用华为HMS MLKit 图像分割 SDK开发一个证件照DIY小程序 引子   上期给大家介绍了如何使用如何用华为HMS MLKit SDK 三十分钟在安卓上开发一个微笑抓 ...

  7. 「1.0」一个人开发一个App,小程序从0到1,起航了

    古有,秦.齐.楚.赵.魏.韩.燕七国争雄:今有,微信.QQ.百度.支付宝.钉钉.头条.抖音七台争霸.古有,白起.李牧.王翦.孙膑.庞涓.赵奢.廉颇驰骋疆场:今有程序员1,程序员2,程序员3…编写代码. ...

  8. Android | 教你如何用华为HMS MLKit SDK 三十分钟在安卓上开发一个微笑抓拍神器

    Android | 只要三十分钟就可以在手机上开发一个微笑抓拍神器!!! 前言 前段时间Richard Yu在发布会上给大家介绍了华为HMS Core4.0,回顾发布会信息请戳: 华为面向全球发布HM ...

  9. 快速入门PaddleOCR,并试用其开发一个搜题小工具

    介绍 PaddleOCR 是一个基于百度飞桨的OCR工具库,包含总模型仅8.6M的超轻量级中文OCR,单模型支持中英文数字组合识别.竖排文本识别.长文本识别.同时支持多种文本检测.文本识别的训练算法. ...

随机推荐

  1. YTU 2455: Pefect 数字

    2455: Pefect 数字 时间限制: 1 Sec  内存限制: 128 MB 提交: 749  解决: 146 题目描述 小明和小林做数字游戏,他们的游戏规则如下: 小明说出一个数字n,小林说出 ...

  2. CentOS/Ubuntu安装GLIBCXX3.4.21

    经过测试“GLIBCXX3.4.21 not find”这篇博文解决了我的问题. 以下是安装步骤:   一.首先查看当前gcc版本 strings /usr/lib/x86_64_linux-gun/ ...

  3. Kappa:比Lambda更好更灵活的实时处理架构

    为了进一步探讨这种批处理和实时处理有效整合在同一系统的架构,我们将在今天的文章中分析Lambda三层结构模型的适用场景,同时暴露出Lambda架构一个最明显的问题:它需要维护两套分别跑在批处理和实时计 ...

  4. centos7无故重启-内核升级

    最近有一台物理服务器,centos7操作系统,无故重启,每天都会发生这种情况: 解决: 升级内核 CentOS 允许使用 ELRepo,这是一个第三方仓库,可以将内核升级到最新版本,使用ELRepo升 ...

  5. 详细讲解:零知识证明 之 ZCash 完整的匿名交易流程

    作者:林冠宏 / 指尖下的幽灵 博客:http://www.cnblogs.com/linguanh/ 掘金:https://juejin.im/user/587f0dfe128fe100570ce2 ...

  6. NOJ2026:Keroro侵略地球(01字典树)

    传送门 题意 略 分析 将n个数插入字典树中,m次查询,取最大值,复杂度\(O(mlogn)\) trick 1.注意题目给的空间,开40刚刚够(62852K) 2.作为01字典树的模板保存了 代码 ...

  7. BestCoder Round #73 (div.2)1002/hdoj5631

    题意: 给出一张 nnn 个点 n+1n+1n+1 条边的无向图,你可以选择一些边(至少一条)删除. 分析: 一张n个点图,至少n-1条边才能保证联通 所以可以知道每次可以删去1条边或者两条边 一开始 ...

  8. 五个demo案例带你学习PHP反序列化漏洞

    一直想研究下php反序列化漏洞,花了几天时间做了个简单的了解..写篇文章记录下. 直白点就是围绕着serialize和unserialize两个函数. 一个用于序列化,一个用于反序列化. 我们通常把字 ...

  9. luogu P1095守望者的逃离【dp】By cellur925

    题目传送门 考虑dp,设f[i]表示到第i时间,能到达的最远距离.因为题目涉及了三种操作:1,补血消耗魔法值:2, 等待增加魔法值:3,直接向前走.而1,3和2,3的操作是可以同时进行没有冲突的,所以 ...

  10. spring AOP excution表达式各符号意思

    execution(*com.sample.service.impl..*.*(..)) 符号 含义 execution() 表达式的主题 第一个“*”符号 表示返回值的类型任意: com.sampl ...