5.1.4 用于音频的MediaStore
本书前面讨论了如何将MediaStore用于图像。我们所学过的大多数只是都可用来存储和检索其他类型的媒体,包括音频。为了提供可靠的机制用于浏览和搜索音频,Android包括一个MediaStore.Audio程序包,其中定义了标准的内容提供器。
1.通过MediaStore访问音频
使用MediaStore提供器访问存储的音频文件与之前使用MediaStore的方式相似。在当前情况下,将使用android.provider.MediaStore.Audio程序包。
演示活动将MediaStore用于音频的最简单方式是给出一个示例应用程序。以下代码创建一个活动,其查询MediaStore以获得任何音频文件,并简单的播放返回的第一个文件。
1 package com.nthm.androidtest; 2 3 import java.io.File; 4 import android.app.Activity; 5 import android.content.Intent; 6 import android.database.Cursor; 7 import android.net.Uri; 8 import android.os.Bundle; 9 import android.os.Environment; 10 import android.provider.MediaStore; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.Button; 14 15 public class AudioPlayer extends Activity implements OnClickListener { 16 private Button playButton; 17 private String []columns={MediaStore.Audio.Media.DATA, 18 MediaStore.Audio.Media._ID, 19 MediaStore.Audio.Media.TITLE, 20 MediaStore.Audio.Media.DISPLAY_NAME, 21 MediaStore.Audio.Media.MIME_TYPE, 22 MediaStore.Audio.Media.ARTIST, 23 MediaStore.Audio.Media.ALBUM, 24 MediaStore.Audio.Media.IS_RINGTONE, 25 MediaStore.Audio.Media.IS_ALARM, 26 MediaStore.Audio.Media.IS_MUSIC, 27 MediaStore.Audio.Media.IS_NOTIFICATION}; 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.audioplayer);
为了使用MediaStore,需要指定想要返回的数据。可以通过使用在android.provider.MediaStore.Audio.Media类中定义的常量创建一个字符串数组来实现该操作。这些常量都是保存在MediaStore中以用于音频的标准字段。
在当前情况下,正在请求DATA列,其中包含了实际的音频文件路径。还将请求内部ID、标题(Title)、显示名称(Display Name)、MIME类型(MIME-Type)、艺术家(Artist)、唱片集(Album)以及它是哪种类型的音频文件(包括警报、音乐、铃声或通知类型)。
其他列(如添加时间(DATA_ADDED)、修改日期(DATE_MODIFIED)、文件大小(SIZE)等)也同样可用。
通过调用活动中的managedQuery方法来查询MediaStore。managedQuery方法接受内容提供器的Uri作为参数。在当前情况下,该内容提供器是音频MediaStore,对应的Uri是android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI。这个Uri指定我们想要存储在SD卡上的音频。如果想要存储在内存中的音频文件,那么将使用android.provider.MediaStore.Audio.Media.INXTERNAL_CONTENT_URI。
除了指向MediaStore的Uri,managedQuery方法还接受想返回的列数组,一条SQL WHERE子句,用于WHERE子句的值以及一条SQL ORDER BY子句。
这个示例不会使用WHERE和ORDER BY子句,所以对于这些参数会传入null。
1 Cursor cursor=managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
managedQuery方法返回一个Cursor对象。Cursor类允许我们与从数据库查询返回的数据集交互。
要做的第一件事实创建几个变量,以保存一些想要从结果中访问的列的编号。这不是绝对必须的,但是获得索引将会非常的方便,从而不必在每次需要他们的时候调用Cursor对象上的方法。获得他们的方法是将想要的类的常量值传递给Cursor对象上的getColumnIndex方法。
1 int fileColumn=cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
第一个索引是包含指向实际音频文件的路径的列索引。通过传入表示该列的常量android.provider.MediaStore.Audio.Media.DATA,可以获得上述索引。
接下来将获得几个其他的索引,他们并非都是实际正在使用的列,这里仅仅是为了演示目的而获得他们。
1 int titleColumn=cursor.getColumnIndex(MediaStore.Audio.Media.TITLE); 2 int displayColumn=cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME); 3 int mimeTypeColumn=cursor.getColumnIndex(MediaStore.Audio.Media.MIME_TYPE);
由MediaStore返回的数据在Cursor对象中可用,且通过行以及列的方式组织起来。通过调用moveToFirst方法和检索它的结果,可以获得返回的第一个结果。如果没有返回任何行,那么该方法将返回一个布尔值false,所以可以将它包含在一条if语句中以确保存在数据。
1 if(cursor.moveToFirst()){
为了获得实际的数据,可以调用Cursor上的“get”方法之一,并传入希望检索的类索引。如果数据预期是一个字符串,那么就调用getStirng。如果数据预期是一个整数,那么调用getInt。对于所有的基本数据类型,都有一个合适的“get”方法。
1 String audioFilePath=cursor.getString(fileColumn); 2 String mimeType=cursor.getString(mimeTypeColumn);
一旦获得了文件的路径和MIME类型,就可以使用它们构造意图,以启动内置的音频播放器的应用程序,并播放文件(或者可以使用之前演示的MediaPlayer类直接播放音频文件)。为了将音频文件的路径转换成可以传递到意图的Uri,可以构造一个File对象并用Uri.fromFile方法来获取Uri。还有其他方法可用来完成同样的事情,但这可能是最简单的方法。
1 Intent intent=new Intent(android.content.Intent.ACTION_VIEW); 2 File newFile=new File(audioFilePath); 3 intent.setDataAndType(Uri.fromFile(newFile), mimeType); 4 startActivity(intent); 5 } 6 } 7 }
以上就完成了如何将MediaStore用于音频的基本说明。
现在让我们更深入一步,创建一个应用程序,从中可以缩小返回的结果,并对他们进行浏览,从而允许用户选择音频文件进行播放。
2.浏览MediaStore中的音频
音频文件(特别是音乐文件)可以按照唱片集、艺术家和流派(Genre)来查找,也可以直接在MediaStore中查找。每个音频文件都有一个Uri,可以使用managedQuery来搜索。
唱片集:android.provider.MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI
艺术家:android.provider.MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI
流派:android.provider.MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI
一下代码演示了如何使用唱片集Uri来查询设备上的所有唱片集:
1 String [] columns={android.provider.MediaStore.Audio.Albums._ID, 2 android.provider.MediaStore.Audio.Albums.ALBUM}; 3 Cursor cursor=managedQuery(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, columns, null, null, null); 4 if(cursor!=null){ 5 while(cursor.moveToNext()){ 6 cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM)); 7 } 8 }
在上述代码中,可以看到其请MediaStore返回_ID和ALBUM列。ALBUM常量表明我们希望返回该唱片集的名称。其他可用的列在android.provider.MediaStore.Audio.Albums类中列出,且都继承自android.provider.BaseColumns和android.provider.MediaStore.Audio.AlbumColumns。
仅仅给定Uri和列的类表来调用managedQuery方法,而使其他的参数为null。这将返回设备上所有可用的唱片集。
最后输出唱片集列表。为了对位于Cursor对象内部的返回列表进行遍历,首先检查该Cursor对象是否包含结果(cursor!=null),然后使用cursor.moveToNext()方法。
3.唱片集浏览应用程序示例
下面是一个示例,其使用上述内容作为一个起点,允许用户查看所有唱片集的名称。用户可以指定他或她想查看哪个唱片集上的歌曲。然后,它将列出歌曲列表,如果用户选择了其中一首,那么它将播放这首歌曲。
1 package com.nthm.androidtest; 2 3 import java.io.File; 4 import android.app.ListActivity; 5 import android.content.Intent; 6 import android.database.Cursor; 7 import android.net.Uri; 8 import android.os.Bundle; 9 import android.provider.MediaStore; 10 import android.view.View; 11 import android.widget.ListView; 12 import android.widget.SimpleCursorAdapter;
没有扩展一个通用的活动,我们将扩展ListActivity,从而能够展现和管理一个基本的ListView对象。
1 public class AudioBrowser extends ListActivity { 2 private Cursor cursor;
接下来将创建几个常量,他们将帮助跟踪用户在应用程序中的位置,而且在用户执行动作时做出适当的响应。以下代码将在currentState变量中跟踪用户,该变量起始位置设置为STATE_SELECT_ALBUM。
1 public static int STATE_SELECT_ALBUM=0; 2 public static int STATE_SELECT_SONG=1; 3 private int currentState=STATE_SELECT_ALBUM;
就像普通的活动一样,其中有一个onCreate方法用来执行初始命令。
1 @Override 2 protected void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState); 4 setContentView(R.layout.audiobrowser);
在设置完布局之后(通过audiobrowser.xml布局XML文件),创建一个字符串数组,标志当运行查询时将从MediaStore返回的列。在这种情况下,它与上述代码片段相同——我们想要返回_ID和唱片集的名称,即ALBUM。他们都是在MediaStore.Audio.Albums类中列出的常量。
1 String[] columns={android.provider.MediaStore.Audio.Albums._ID, 2 android.provider.MediaStore.Audio.Albums.ALBUM};
仅仅用表示唱片集的Uri以及列的列表来调用managedQuery方法,其他参数都设为null,这应该会返回一个所有可用唱片集的列表。
1 cursor=managedQuery(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, columns, null, null, null);
一旦这么操作,就会返回一个包含查询结果的Cursor对象。
由于使用了ListActivity,因此能够使它自动管理数据列表。可以使用setListAdapter犯法将Cursor对象绑定到ListView对象。
首先创建一个字符串数组,他们表示想要显示的Cursor对象中的列名。在当前的情况下,我们只想要唱片集、的名称。即MediaStore.Audio.Albums.ALBUM使对应的常量。
接下来,列出将用来显示来自这些列的数据的View对象。由于现在只有一列,因此,只需要一个View对象,即android.R.id.text1。这个View对象是可用的,因为他是下一步将要使用的android.R.layout.simple_list_item_1布局的一部分。
最后调用setListAdapter方法,并传入一个内部创建的SimpleCursorAdapter。SimpleCursorAdapter是一个简单的适配器,它将Cursor对象包含的数据转换给ListActivity。在创建SimpleCursorAdapter时,传入作为上下文的活动(this)、一个已经定义的标准ListView布局(android.R.layout.simple_list_item_1)、包含数据的Cursor对象以及刚刚定义的两个数组。
1 String []displayFields=new String[]{MediaStore.Audio.Albums.ALBUM}; 2 int [] displayerViews=new int[]{android.R.id.text1}; 3 setListAdapter(new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, displayFields, displayerViews)); 4 }
如果运行这段代码,将获得设备上可用的唱片集的一个简单的类表。不过我们打算更深一步,允许用户选择一个唱片集。
为了允许用户实际的选择一个唱片集,需要重写由父类ListActivity提供的默认方法onListItemClick。
1 @Override 2 protected void onListItemClick(ListView l, View v, int position, long id) { 3 super.onListItemClick(l, v, position, id); 4 if(currentState==STATE_SELECT_ALBUM){
当选择类表中的的唱片集时将调用该方法。由于currentState变量开始时是STATE_SELECT_ALBUM,因此第一次调用该方法时,它应该为true。
传入在列表中选定的唱片集位置,同时Cursor对象将利用该位置,通过调用moveToPosition方法获知它是哪个唱片集。
1 if(cursor.moveToPosition(position)){
假定moveToPosition方法成功返回,我们将重新开始查询MediaStore。然而,由于这一次想要访问单个媒体文件,因此是在MediaStore.Audio.Media.EXTERNAL_CONTENT_URI上运行managedQuery方法。
首先选择想要返回的列。
1 String [] columns={MediaStore.Audio.Media.DATA, 2 MediaStore.Audio.Media._ID, 3 MediaStore.Audio.Media.TITLE, 4 MediaStore.Audio.Media.DISPLAY_NAME, 5 MediaStore.Audio.Media.MIME_TYPE};
接下来需要为查询构造SQL WHERE 子句。因为我们只想选择属于特定唱片集的媒体文件,所以应在WHERE子句中指明这一点。
普通SQL中的WHERE子句如下表示:
WHERE album=‘album name’
由于正在使用managedQuery,因此不需要WHERE关键字,同时也不需要传入它的等价物。相反,我们采用一个“?”符号进行替代。因此对于上述版本,该字符串将如下所示:
album=?
由于正在使用常量,并不知道列的实际名称,因此将使用该常量来构造WHERE子句。
1 String where=android.provider.MediaStore.Audio.Media.ALBUM+"=?";
为了完成WHERE子句,需要数据来替换WHERE子句中的“?”符号。这将是一个字符串数组,其中的每个字符串对应一个使用的“?”符号。在当前情况下,我们需要使用选择的唱片集名称。因为Cursor对象位于正确的位置,所以只需要对正确的列调用Cursor对象上的getString方法,可以通过对列名调用Cursor对象上的getColumnIndex方法来获得正确的列。
1 String whereVal[]={cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM))};
然后可以指定我们希望以特定列的值对结果进行排序。为此,需要创建一个变量,其包含了希望结果按其顺序排列的列名。
1 String orderBy=android.provider.MediaStore.Audio.Media.TITLE;
最后,运行managedQuery方法,传入Uri、列、WHERE子句的变量、WHERE子句的数据以及ORDER BY 子句的变量。
1 cursor=managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, columns, where, whereVal, orderBy);
同样,将使用ListActivity的各种方法来管理Cursor对象和展示列表中的结果。
1 String []displayFields=new String[]{MediaStore.Audio.Media.DISPLAY_NAME}; 2 int [] displayerViews=new int[]{android.R.id.text1}; 3 setListAdapter(new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, displayFields, displayerViews));
最后要做的事情是将currentState变量改为STATE_SELECT_SONG,从而当下一次调用该方法时,我们将跳过所有这些步骤,因为用户将选定一首歌而不是唱片集。
1 currentState=STATE_SELECT_SONG; 2 } 3 }else if(currentState==STATE_SELECT_SONG){
当用户选择唱片集并从列表中选定一首歌时,他或她将进入该方法的这部分,因为currentState将等于STATE_SELECT_SONG。
1 if(cursor.moveToPosition(position)){
正如前面所做的那样,调用Cursor对象上相同的moveToPosition方法,我们可以获得实际选择的这首歌。在这种情况下,我们会获得包含文件路径的列以及该文件的MIME类型。我们将它转换成一个File对象,并创建一个意图以启动内置的音乐播放器应用程序。
1 int fileColumn=cursor.getColumnIndex(MediaStore.Audio.Media.DATA); 2 int mimeTypeColumn=cursor.getColumnIndex(MediaStore.Audio.Media.MIME_TYPE); 3 String audioFilePath=cursor.getString(fileColumn); 4 String mimeType=cursor.getString(mimeTypeColumn); 5 Intent intent=new Intent(android.content.Intent.ACTION_VIEW); 6 File file=new File(audioFilePath); 7 intent.setDataAndType(Uri.fromFile(file), mimeType); 8 startActivity(intent); 9 } 10 } 11 } 12 }
下面是用于上述代码的布局XML文件,你将注意到它包含了一个ID为list的ListView对象。这是用于我们正在扩展的ListActivity的默认的ID。
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" 5 > 6 <ListView 7 android:id="@+android:id/list" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content"></ListView> 10 </LinearLayout>
现在就完成了该示例应用程序,它使得我们能够通过使用MediaStore浏览唱片集,并选择歌曲进行播放。以可以使用非常类似的方法来构建应用程序,从而能够基于艺术家和流派来浏览和选择音乐。
posted on 2014-08-27 16:59 宁静致远,一览众山小 阅读(908) 评论(0) 编辑 收藏 举报