通过调用安卓的MediaPlayer可以直接完成Mp3等主流音频的播放,同时利用ContentResolver与Cursor可以直接读取安卓内在数据库的信息,直接获取当前sdcard中所有音频的列表,无须像《【Android】内存卡图片读取器,图库app》(点击打开链接)一样利用原始的Java代码去遍历整个sdcard卡,直接调用安卓固有的类既便捷又快速。最后,读取出来的Mp3可以通过适配器直接加载到ListView列表,做出如下所示的内存卡Mp3播放器app效果。本app在自己的真实的16G内存卡上真机测试通过。

首先,假设在内存卡上有如下的5个mp3文件,这里顺带提一句,利用DDMS拷贝文件到内存卡的时候注意,亲测发现,无法送PC上一个中文命名的文件到安卓虚拟机AVD,只能送英文文件。不嫌麻烦,可以先改名再传输,到安卓虚拟机AVD再改名。或者直接用英文歌曲。DDMS的使用可以参考《【Android】把外部文件拷贝的AVD安卓模拟器上的sdcard上,并且在AVD中浏览sdcard的文件》(点击打开链接)。


之后,如下图所示,实现一个mp3播放器的大致功能,可以调节音量,上一首、下一首、播放等等,在没有选定音乐这些按钮禁用。


制作过程如下:

1、首先在res\values\strings.xml设置各个按钮与菜单的字体如下:

<?xml version="1.0" encoding="utf-8"?><resources>    <string name="app_name">内存卡mp3播放器</string>    <string name="action_settings">Settings</string>    <string name="button1">上一首</string>    <string name="button2">暂停</string>    <string name="button3">停止</string>    <string name="button4">下一首</string>    <string name="menu_author">作者:yongh701</string>    <string name="menu_exit">退出</string></resources>
2、其次,如《【Android】日期拾取器、时间拾取器与菜单》( 点击打开链接)一样,对res\menu\main.xml进行修改,设定一个很简单的菜单:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >    <item        android:id="@+id/menu_exit"        android:title="@string/menu_exit"/>    <item android:title="@string/menu_author"/></menu>
3、然后,由于设置sdcard的操作与改变系统的媒体音量,需要到AndroidManifest.xml申请权限,此文件修改之后如下:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.mp3player"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="18" />    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 要求向SDCard读取数据权限 -->    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 要求向SDCard写入数据权限 -->    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- 要求改变音量的权限 -->    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name="com.mp3player.MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application></manifest>

4、之后,修改res\layout\activity_main.xml对MainActivity.java进行布局。

思想如下图:


在一个自上而下垂直的线性布局下,摆两个横向的水平线性布局与一个列表视图ListView,宽度皆匹配父布局。其中,第一个横向的水平线性布局通过《【Android】利用相对布局布置更新软件的style为主题对话框的Activity,利用layout_weight属性对表格布局的行划分》(点击打开链接)提及到的方式,等分放置四个按钮,其次,在第二个横向的水平线性布局,放置一个仅包裹内容的,用于文字显示音量的TextView与一个进度条SeekBar。这两个横向的水平线性布局的高度都是仅包裹内容即可。最后的ListView的高度直接匹配父布局。因此,代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal" >        <Button            android:id="@+id/button1"            android:layout_width="0dip"            android:layout_height="match_parent"            android:layout_weight="1"            android:text="@string/button1" />        <Button            android:id="@+id/button2"            android:layout_width="0dip"            android:layout_height="match_parent"            android:layout_weight="1"            android:text="@string/button2" />        <Button            android:id="@+id/button3"            android:layout_width="0dip"            android:layout_height="match_parent"            android:layout_weight="1"            android:text="@string/button3" />        <Button            android:id="@+id/button4"            android:layout_width="0dip"            android:layout_height="match_parent"            android:layout_weight="1"            android:text="@string/button4" />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal" >        <TextView            android:id="@+id/textView1"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <SeekBar            android:id="@+id/seekBar1"            android:layout_width="match_parent"            android:layout_height="wrap_content" />    </LinearLayout>    <ListView        android:id="@+id/listView1"        android:layout_width="match_parent"        android:layout_height="match_parent" >    </ListView></LinearLayout>

5、最后,是本app实现的核心,对MainActivity.java进行编写,大体上分为三部分:各个组件的代码实现、菜单的实现与返回按键的监听。之所以对返回物理按钮的监听,是因为需要要求,用户在按返回物理按钮是彻底退出程序。退出程序时候,还要释放被本app占用系统的MediaPlayer,因此还要重写onDestory方法,释放资源。否则在程序退出之后,播放的音乐依然会“绕梁三日”。

在组件代码实现的部分,还有如下细分,注册各个组件之后,可以直接利用ContentResolver contentResolver = getContentResolver();获取安卓系统的数据接口,这个数据接口是安卓系统内部的数据库,里面存放着几张记录当前系统所有媒体,类似图片、音乐、视频等信息的表,通过Cursor这个数据库的迭代器,或者叫游标,反正是iterator对表进行遍历,可以直接取出媒体的信息,这里取走最关键的信息,无须用Java原始的遍历方法《【Java】读取其下所有文件夹与文件的路径》(点击打开链接),迭代求出各个音乐媒体的路径,产生巨大的时间复杂度。

package com.mp3player;import java.util.ArrayList;import java.util.Collections;import android.media.AudioManager;import android.media.MediaPlayer;import android.os.Bundle;import android.provider.MediaStore;import android.app.Activity;import android.content.ContentResolver;import android.content.Context;import android.database.Cursor;import android.view.KeyEvent;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.ListView;import android.widget.SeekBar;import android.widget.SeekBar.OnSeekBarChangeListener;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity {private MediaPlayer mediaPlayer = new MediaPlayer();private ListView listView1;private ArrayList<String> audioList;// 存放音乐路径的动态数组private int currentAudioId;private Button button1;private Button button2;private Button button3;private Button button4;private TextView textView1;private SeekBar seekBar1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 注册各个组件listView1 = (ListView) findViewById(R.id.listView1);button1 = (Button) findViewById(R.id.button1);button2 = (Button) findViewById(R.id.button2);button3 = (Button) findViewById(R.id.button3);button4 = (Button) findViewById(R.id.button4);textView1 = (TextView) findViewById(R.id.textView1);seekBar1 = (SeekBar) findViewById(R.id.seekBar1);// 初始状态“暂停/播放”按钮不可用,因为没有选定音乐button1.setEnabled(false);button2.setEnabled(false);button3.setEnabled(false);button4.setEnabled(false);// 加载音乐资源ContentResolver contentResolver = getContentResolver();Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,MediaStore.Audio.Media.DEFAULT_SORT_ORDER);audioList = new ArrayList<String>();for (cursor.moveToFirst(); !(cursor.isAfterLast()); cursor.moveToNext()) {String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));audioList.add(path);}Collections.sort(audioList);ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, audioList);listView1.setAdapter(arrayAdapter);listView1.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> arg0, View arg1,int position, long arg3) {currentAudioId = position;String path = audioList.get(currentAudioId);playMusic(path);button1.setEnabled(true);button3.setEnabled(true);button4.setEnabled(true);}});Toast.makeText(MainActivity.this,"音乐加载完成,共" + audioList.size() + "首音乐", Toast.LENGTH_SHORT).show();// 调节音量的功能final AudioManager audioManager = (AudioManager) MainActivity.this.getSystemService(Context.AUDIO_SERVICE);// 音乐管理器必须使用final类在OnCreate中定义MainActivity.this.setVolumeControlStream(AudioManager.STREAM_MUSIC);// 调节的是媒体音量seekBar1.setMax(audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC));// 设置音量条的最大值为系统媒体音量的最大值int volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);// 当前媒体音量seekBar1.setProgress(volume);textView1.setText("音量:" + volume);seekBar1.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {@Overridepublic void onStopTrackingTouch(SeekBar arg0) {}@Overridepublic void onStartTrackingTouch(SeekBar arg0) {}@Overridepublic void onProgressChanged(SeekBar arg0, int progress,boolean arg2) {textView1.setText("音量:" + progress);audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,progress, AudioManager.FLAG_PLAY_SOUND);// 设置改变之后的音量}});// 各个按钮的点击监听// 上一首button1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {currentAudioId--;if (currentAudioId < 0) {currentAudioId = 0;}String path = audioList.get(currentAudioId);playMusic(path);}});// 暂停/播放按钮button2.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {if (mediaPlayer.isPlaying()) {mediaPlayer.pause();button2.setText("继续");} else {mediaPlayer.start();button2.setText("暂停");}}});// 停止按钮button3.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubif (mediaPlayer.isPlaying()) {mediaPlayer.stop();}button2.setEnabled(false);button3.setEnabled(false);}});// 下一首按钮button4.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubcurrentAudioId++;if (currentAudioId > audioList.size() - 1) {currentAudioId = audioList.size() - 1;}String path = audioList.get(currentAudioId);playMusic(path);}});}// 播放音乐public void playMusic(String path) {try {if (mediaPlayer.isPlaying()) {mediaPlayer.stop();}mediaPlayer.reset();mediaPlayer.setDataSource(path);mediaPlayer.prepare();mediaPlayer.start();} catch (Exception e) {e.printStackTrace();}button2.setEnabled(true);button2.setText("暂停");button3.setEnabled(true);Toast.makeText(MainActivity.this, "播放:" + path + "", Toast.LENGTH_SHORT).show();}// 退出程序时,释放当前音乐资源protected void onDestroy() {super.onDestroy();if (mediaPlayer.isPlaying()) {mediaPlayer.stop();}mediaPlayer.release();}// 创建menu的方法,没有该方法,不会在右上角设置菜单。@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// 设置menu界面为res\menu\menu.xmlgetMenuInflater().inflate(R.menu.main, menu);return true;}// 处理菜单事件public boolean onOptionsItemSelected(MenuItem item) {// 得到当前选中的MenuItem的ID,int item_id = item.getItemId();switch (item_id) {// 设置id为menu_exit的菜单子项所要执行的方法。case R.id.menu_exit:System.exit(0);// 结束程序break;}return true;}// 对物理按钮的监听@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {switch (keyCode) {case KeyEvent.KEYCODE_BACK:System.exit(0);break;}return super.onKeyDown(keyCode, event);}}
之后,对于读取出来的文件信息,直接用适配器加载到ListView列表。随后对各个按钮的监听没什么好说的,记得播放的音乐时要先释放当前正在播放的音乐再上新曲,安卓系统不会自己覆盖播放。这里还需要处理一个加载音乐失败的异常。

在音量处理部分,需要自己创建一个音乐管理器AudioManager,此管理器必须在OnCreate方法中以final的形式定义,否则会出现如下图的错误:


通过音乐管理器能够获取与改变当前系统的媒体音量,可以把这个音量值加载到进度条,进度条的使用在《【Android】进度条与线程之间的消息处理》(点击打开链接)中已经讲过,这里不再赘述。

我还上了一份源码给大家:http://download.csdn.net/detail/yongh701/8932343,欢迎交流,上次感谢网友提醒可以通过安卓系统内部的数据库拿到sdcard卡的媒体信息,我才省悟无须迭代这么麻烦。

更多相关文章

  1. Android切换按钮与沉浸式状态栏的实现
  2. android-从音频数据库获取音乐数据
  3. android实现可拖动按钮
  4. android音乐播放器-------使用android系统自带的数据库
  5. [置顶] 我的Android进阶之旅------>关于android:layout_weight属
  6. 转:一个Demo学完Android中所有的服务
  7. android在service中播放音乐
  8. Android搜索框输入内容点击键盘的搜索按钮进行搜索
  9. Android音量调节的实现(RingtoneManager和RingerVolumePreferenc

随机推荐

  1. 推json敲出可观察的数组
  2. Angularjs在ng-repeat中找到最后一个可见
  3. Angularjs:如何将范围变量传递给指令?
  4. IE 浏览器各个版本 JavaScript 支持情况
  5. 当页面上有多个按钮时,按钮样式在页面加载
  6. 从占用转义字符的字符数组创建字符串
  7. 无法让.click()在禁用的textarea上工作
  8. AJAX应该使用hashtag /#!/或不呢?
  9. Div高度为图像高度,图像宽度为div宽度
  10. 环回 - 在“保存后”挂钩中覆盖之前查看