最近做了一个android音乐播放器,个人感觉最难的就是“后台播放”以及有关“播放列表”的部分,但是总算是找到了实现的方式。不同的人实现的方式可能不一样,这里我就分享一下自己对“播放列表”这个模块的一些实现方法,“后台播放”会在下一篇博文中进行介绍,希望大家也能分享一下自己的一些思路。
android使用ContentProvider来支持不同应用程序的数据共享,为了方便其他应用程序对sdcard中的数据进行操作,sdcard也提供了ContentProvider接口,这里就以访问音频文件为例,视频以及图片的操作也类似,这里就不在赘述。
访问sdcard中的音频文件的URI为MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,为了使播放列表显示所以音乐文件的信息,这里需要查询sdcard里的音频文件,并把查询到的信息保存在Cursor中,具体代码如下:
<pre>Cursor c = this .getContentResolver().</pre>
<pre>query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,</pre> /*这个字符串数组表示要查询的列*/ |
new String[]{MediaStore.Video.Media.TITLE, //音乐名
MediaStore.Audio.Media.DURATION, //音乐的总时间
MediaStore.Audio.Media.ARTIST, //艺术家
MediaStore.Audio.Media._ID, //id号
MediaStore.Audio.Media.DISPLAY_NAME, //音乐文件名
MediaStore.Audio.Media.DATA //音乐文件的路径
}, null , //查询条件,相当于sql中的where语句
null , //查询条件中使用到的数据
null ); //查询结果的排序方式
|
通过MediaStore.Audio.Media.XXX来访问音乐文件的一些信息,这里只列出了一部分,可以根据需要进行增添和删除。
至此,Cursor c就已经保存了sdcard内所以音频文件的信息,下面的操作就是围绕这个Cursor展开的。
首先定义三个数组:
private int [] _ids; //存放音乐文件的id数组
private String[] _titles; //存放音乐文件的标题数组
private String[] _path; //存放音乐文件的路径
|
_ids保存了所有音乐文件的_ID,用来确定到底要播放哪一首歌曲,_titles存放音乐名,用来显示在播放界面,而_path存
放音乐文件的路径(删除文件时会用到)。
接下来再定义一个变量,用来定位选择的是哪一首音乐:
private int pos;
接下来将音乐文件的信息存放在相应的数组中:
c.moveToFirst(); _ids = new int [c.getCount()];
_titles = new String[c.getCount()];
_path = new String[c.getCount()];
for ( int i=0;i<c.getCount();i++){
_ids[i] = c.getInt(3);
_titles[i] = c.getString(0);
_path[i] = c.getString(5).substring(4);
c.moveToNext();
} |
有人可能会问为什么获取路径的格式是_path[i]=c.geString(5).substring(4)?因为MediaStore.Audio.Media.DATA
得到的内容格式为/mnt/sdcard/[子文件夹名/]音乐文件名,而我们想要得到的是/sdcard/[子文件夹名]音乐文件名,
所以要做相应的裁剪操作。
接下来把Cursor中的信息显示到listview中:
MusicListAdapter adapter = new MusicListAdapter( this , c);
listview.setAdapter(adapter); |
MusicListAdapter是一个自定义的Adapter,继承自BaseAdapter,这里只贴出代码,不做讲解。
package com.alex.video; import android.content.Context; import android.database.Cursor; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; public class MusicListAdapter extends BaseAdapter{
private Context myCon;
private Cursor myCur;
private int pos=-1;
public MusicListAdapter(Context con,Cursor cur){
this .myCon = con;
this .myCur = cur;
}
@Override
public int getCount() {
return this .myCur.getCount();
}
@Override
public Object getItem( int position) {
return position;
}
@Override
public long getItemId( int position) {
return position;
}
@Override
public View getView( int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(myCon).inflate(R.layout.musiclist,
null );
myCur.moveToPosition(position);
TextView videoTitle = (TextView)convertView.findViewById(R.id.musictitle);
if (myCur.getString(0).length()>24){
try {
String musicTitle = bSubstring(myCur.getString(0).trim(),24);
videoTitle.setText(musicTitle);
} catch (Exception e) {
e.printStackTrace();
}
} else {
videoTitle.setText(myCur.getString(0).trim());
}
TextView videoArtist = (TextView)convertView.findViewById(R.id.musicartist);
if (myCur.getString(2).equals( "<unknown>" )){
videoArtist.setText( "未知艺术家" );
} else {
videoArtist.setText(myCur.getString(2));
}
TextView videoTime = (TextView)convertView.findViewById(R.id.musictime);
videoTime.setText(toTime(myCur.getInt(1)));
ImageView videoItem = (ImageView)convertView.findViewById(R.id.musicitem);
videoItem.setImageResource(R.drawable.item);
return convertView;
}
/*时间格式转换*/
public String toTime( int time) {
time /= 1000;
int minute = time / 60;
int hour = minute / 60;
int second = time % 60;
minute %= 60;
return String.format( "%02d:%02d" , minute, second);
}
/*字符串裁剪*/
public static String bSubstring(String s, int length) throws Exception
{
byte [] bytes = s.getBytes( "Unicode" );
int n = 0; // 表示当前的字节数
int i = 2; // 要截取的字节数,从第3个字节开始
for (; i < bytes.length && n < length; i++)
{
// 奇数位置,如3、5、7等,为UCS2编码中两个字节的第二个字节
if (i % 2 == 1)
{
n++; // 在UCS2第二个字节时n加1
}
else
{
// 当UCS2编码的第一个字节不等于0时,该UCS2字符为汉字,一个汉字算两个字节
if (bytes[i] != 0)
{
n++;
}
}
}
// 如果i为奇数时,处理成偶数
if (i % 2 == 1)
{
// 该UCS2字符是汉字时,去掉这个截一半的汉字
if (bytes[i - 1] != 0)
i = i - 1;
// 该UCS2字符是字母或数字,则保留该字符
else
i = i + 1;
}
return new String(bytes, 0, i, "Unicode" );
}
} |
在上一篇随笔中,我介绍了如何在程序中查询sdcard内的多媒体文件,并且显示到播放列表中,但是,如果在sdcard内删除、增加一些多媒体文件,如何让播放列表也更新呢,这里我分享一下自己在项目中的一些解决方法,希望对大家有所帮助。
首先,我简单介绍一下android是如何扫描sdcard内的多媒体信息的,详细请阅读stay的博文:http://www.cnblogs.com/stay/articles/1957571.html
当android的系统启动的时候,系统会自动扫描sdcard内的多媒体文件,并把获得的信息保存在一个系统数据库中,以后在其他程序中如果想要访问多媒体文件的信息,其实就是在这个数据库中进行的,而不是直接去sdcard中取,理解了这一点以后,问题也随着而来:如果我在开机状态下在sdcard内增加、删除一些多媒体文件,系统会不会自动扫描一次呢?答案是否定的,也就是说,当你改变sdcard内的多媒体文件时,保存多媒体信息的系统数据库文件是不会动态更新的。
那么如何让多媒体数据库中的数据更新呢?我们可以采用广播机制来实现:在应用程序中发送一个广播,让android系统扫描sdcard并更新多媒体数据库
private void scanSdCard(){
IntentFilter intentfilter = new IntentFilter( Intent.ACTION_MEDIA_SCANNER_STARTED);
intentfilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
intentfilter.addDataScheme( "file" );
scanSdReceiver = new ScanSdReceiver();
registerReceiver(scanSdReceiver, intentfilter);
sendBroadcast( new Intent(Intent.ACTION_MEDIA_MOUNTED,
|
其中ScanSdReceiver是一个自定义的广播接收器,继承自BroadCastReceiver,因为android系统开始扫描sdcard以及扫描完毕时都会发送一个系统广播来表示当前扫描的状态,这样我们就可以很方便通过判断当前的扫描状态加一些自己的逻辑操作,ScanSdReceiver的代码如下:
public class ScanSdReceiver extends BroadcastReceiver {
private AlertDialog.Builder builder = null ;
private AlertDialog ad = null ;
private int count1;
private int count2;
private int count;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_MEDIA_SCANNER_STARTED.equals(action)){
Cursor c1 = context.getContentResolver()
.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME },
null , null , null );
count1 = c1.getCount();
System. out .println( "count:" +count);
builder = new AlertDialog.Builder(context);
builder.setMessage( "正在扫描存储卡..." );
ad = builder.create();
ad.show();
} else if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)){
Cursor c2 = context.getContentResolver()
.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME },
null , null , null );
count2 = c2.getCount();
count = count2-count1;
ad.cancel();
if (count>=0){
Toast.makeText(context, "共增加" +
count + "首歌曲" , Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "共减少" +
count + "首歌曲" , Toast.LENGTH_LONG).show();
}
}
}
} |
这里我们定义了两个Cursor对象,分别用来存储扫描前后的多媒体信息,并给出相应的提示。
以前有很多朋友问过我,为什么扫描以后播放列表中的数据条数没有发生相应的改变。要实现播放列表在扫描后更新,必须重新读取多媒体信息到Cursor中,并且重新设置adapter,最后还要调用XXXAdapter.notifyDataSetChanged()来通知UI更新。(可以参考第一张的内容)
上面的操作都是手动在SDCARD中添加或着删除多媒体文件,下面介绍如何在列表中删除SDCARD中的多媒体文件。
在上一篇随笔中,我们使用了系统提供的ContentProvider来查询sdcard中的多媒体文件,我们同样可以使用这个ContentProvider来进行删除操作:
private void deleteMusic( int position){
this .getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
MediaStore.Audio.Media._ID + "=" + _ids[position],
null );
} |
其中“多媒体文件的ID”可以从_ids数组中取得(关于_ids数组请参考第一张的内容)。这样,我们是否就已经从SDCARD中删除了指定_ID的多媒体文件了呢? 其实当你打开FileExplorer时会发现,原来的文件并没有被删除,— —!杯具,搞了半天文件还在,我第一次遇到这个问题的时候也是纠结了老半天。。。
为什么没有被删除,原因是上面的删除操作只是删除了系统多媒体数据库中的相应记录,而并没有删除SDCAED中的文件(注意:多媒体信息数据库和SDCARD中的多媒体文件并不会自动保持同步),这个时候如果再次扫描SDCARD,你会发现刚才从播放列表中删除的行会再次出现。
其实要想真正的从SDCARD中删除多媒体文件并不难,可能有朋友会想到这样的方法:遍历SDCARD中的多媒体文件,然后把想要删除的文件和其他文件逐一比较,找到文件路径,最后进行删除。这种方法是可以实现删除操作的,不过效率很低,如果SDCARD中的文件夹以及文件很多,也不知道要用掉多少时间。。
在上一张中我们从多媒体数据库中读出来一项很重要的信息:MediaStore.Audio.Media.DATA
并且取得里面的字串并存放在了_path数组中,最终的数据格式为:/SDCARD/[子文件夹名]文件名,又了这个路径,我们就可以很方便的从SDCARD中删除多媒体文件了
private void deleteMusicFile( int position){
File file = new File(_path[position]);
file.delete();
} |
同样,删除文件后要想播放列表同步更新,也必须执行刚才介绍的一系列操作。
相关推荐
在Android平台上,开发人员经常需要处理文件操作,特别是在涉及到用户数据存储或媒体展示时。本教程将详细讲解如何从SD卡(外部存储)中读取文件信息,并将其中的图片加载到`ImageView`组件中进行显示。这涉及到...
在Android平台上,对SDCard(外部存储)进行文件操作是应用程序与用户交互的重要部分,尤其对于需要存储大量数据或媒体文件的应用来说。本教程将详细解释如何在Android中读写SDCard文件,非常适合初学者入门。 首先...
在Android系统中,将中文文件镜像上传到SD卡是一个常见的需求,特别是在处理系统更新、应用安装或数据备份时。本文将深入探讨如何在Android设备上实现这一操作,重点介绍与"android"、"镜像"、"中文"以及"UltraISO...
在Android系统中,外置SDCard(也称为外部存储)是设备上用于扩展存储空间的区域,用户可以在这个空间上存储各种数据,如媒体文件、应用程序数据等。本篇文章将详细探讨如何在Android应用中实现对外置SDCard的读取、...
在AndroidManifest.xml文件中,你需要添加`READ_EXTERNAL_STORAGE`和`WRITE_EXTERNAL_STORAGE`权限,以便应用可以读写SD卡上的文件: ```xml <uses-permission android:name="android.permission.READ_EXTERNAL_...
本篇将详细介绍在Android 2.3及更高版本中,如何获取和操作Android设备的内存目录以及SDCard目录。 首先,我们要理解Android的存储架构。内部存储主要用于存储应用私有的数据,这些数据对其他应用是不可见的。而...
最后,需要修改`/system/etc/vold.conf`文件中的相关配置,确保指向正确的SDCard设备路径。以下是一个具体的示例配置: ```plaintext ## vold configuration file for the 'generic' target volume_sdcard { ## ...
在Android开发或者测试过程中,有时候我们需要对安卓模拟器的存储空间进行操作,比如添加应用程序、媒体文件等。这时,就需要了解如何打开并修改安卓模拟器的`sdcard.img`文件。`sdcard.img`文件是Android模拟器中...
在Android系统中,外部存储设备通常指的是SD卡或者内置的外部存储空间,它为应用程序提供了扩展存储能力,以便存储较大的用户数据、媒体文件等。本文将深入探讨如何在Android应用中进行外部存储文件操作,特别是在SD...
在Android系统中,sdcard是一个虚拟的存储空间,模拟了真实设备上的外部存储,用于存储用户数据、应用程序文件和媒体内容。在模拟器中,sdcard的位置默认位于用户的Android SDK目录下的`<android-sdk>\tools\...
接着,当文件成功推送到模拟器的sdcard后,可以在模拟器中使用DevTools中的MediaScanner服务来扫描媒体文件,从而使得新增的媒体文件能够在模拟器上的音乐播放器中被识别和播放。此外,也可以通过在模拟器上执行adb ...
要在Android应用中使用SDCard,首先需要在AndroidManifest.xml文件中声明相应的权限: 1. **访问SDCard权限**:允许应用在SDCard中创建与删除文件。 ```xml <uses-permission android:name="android.permission....
在Android平台上,播放SDcard(外部存储)中的所有音频文件是一项常见的任务,尤其对于音乐播放器或者媒体应用来说。这个任务涉及到多个知识点,包括文件系统操作、多媒体处理、以及Android的权限管理。以下是对这些...
压缩包中的资源可能包含示例代码,用于演示如何在Android应用中实现内部存储和SDCard存储的文件操作,包括创建、读取、写入和删除文件。通过阅读和理解这些代码,开发者可以更好地掌握Android文件存储的实践技巧。 ...
在Android系统中,SDCard(Secure Digital Card)是设备存储扩展和数据交换的重要部分,尤其在存储用户数据、应用程序缓存、媒体文件等方面扮演着重要角色。本资料主要围绕Android平台下对SDCard进行文件读写和容量...
总之,理解和熟练操作Android的SDCard是开发过程中不可或缺的技能,这涉及到文件管理、权限控制以及对新版本Android特性的适应。无论是物理SDCard还是emulated SDCard,都需遵循Android的安全存储规则,确保用户数据...
Sdcard(Secure Digital Card)是Android设备常用的外部存储器,用于存放大量的非敏感数据,如媒体文件、应用缓存等。本篇文章将深入探讨Android如何在Sdcard上进行数据存储,并通过一个简单的示例来说明。 首先,...
内部存储通常用于应用私有的数据,而外部存储,也就是常说的SDCard,主要用于共享媒体文件和大型数据。在API 29及以上版本,Android引入了分区存储,进一步区分了用户可访问的公共目录和应用私有的目录。 1. 获取...
1. **Android权限管理**:在AndroidManifest.xml文件中,你需要添加`WRITE_EXTERNAL_STORAGE`和`READ_EXTERNAL_STORAGE`权限,以允许应用访问SDCard。自Android 6.0 (API级别23)起,这些权限在安装时不再自动授予,...