【Android】利用安卓的数据接口、多媒体处理编写内存卡Mp3播放器app

通过调用安卓的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;

	@Override
	protected 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() {
			@Override
			public 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() {
			@Override
			public void onStopTrackingTouch(SeekBar arg0) {
			}

			@Override
			public void onStartTrackingTouch(SeekBar arg0) {
			}

			@Override
			public 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() {
			@Override
			public void onClick(View arg0) {
				currentAudioId--;
				if (currentAudioId < 0) {
					currentAudioId = 0;
				}
				String path = audioList.get(currentAudioId);
				playMusic(path);
			}
		});
		// 暂停/播放按钮
		button2.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				if (mediaPlayer.isPlaying()) {
					mediaPlayer.pause();
					button2.setText("继续");
				} else {
					mediaPlayer.start();
					button2.setText("暂停");
				}
			}
		});
		// 停止按钮
		button3.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				if (mediaPlayer.isPlaying()) {
					mediaPlayer.stop();
				}
				button2.setEnabled(false);
				button3.setEnabled(false);
			}
		});
		// 下一首按钮
		button4.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				currentAudioId++;
				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的方法,没有该方法,不会在右上角设置菜单。
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// 设置menu界面为res\menu\menu.xml
		getMenuInflater().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;
	}

	// 对物理按钮的监听
	@Override
	public 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卡的媒体信息,我才省悟无须迭代这么麻烦。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-07 17:44:28

【Android】利用安卓的数据接口、多媒体处理编写内存卡Mp3播放器app的相关文章

Android练习项目 Mp3播放器实现(一)

对于Android的学习,需要掌握的东西有点多,需要我们认真,小心,不断的进取.前天突然有个想法,觉得Mp3播放器是一个可以练习的项目,于是在网上搜了下,发现有人已经写了博客,看了他们的博客后,我觉得他们说的一点很对,Mp3播放器基本用到了Android里面的许多知识点,做完这个过后,可能对于Android整个架构有了一定了解,我于是也想尝试下,于是准备边做,编写博客,来记录自己开发的过程,这个也许叫作项目开发日志吧. 第一个我的想法是先做:本地音乐播放器. 于是我用了个粗浅的方法来加载mp3文

android开发之MediaPlayer+Service MP3播放器

http://blog.csdn.net/zzy916853616/article/details/6450753 [java] view plaincopy import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.apps.service.Player

Android练习项目 Mp3播放器实现(二)

在经过几天的折腾之后,迎来了Mp3播放器的第二篇博客,这篇文章主要将如何实现播放器的播放,下一首,上一首,顺序播放,循环播放,随机播放,单曲循环等操作. 其实操作的过程中主要是通过参数的传递来实现功能的. 在activity里面互相传参有很多中方法,但对于activity和service进行交互有些方法并不适合,通过对Broadcast和BroadcastReceiver等操作,来控制播放的实现. 我们先设定好广播,然后通过发送信号来进行传递数据. 主要在activity里面动态注册广播,通过传

安卓MP3播放器开发实例(1)之音乐列表界面

学习安卓开发有一年了,想想这一年的努力,确实也收获了不少.也找到了比較如意的工作. 今天准备分享一个以前在初学阶段练习的一个项目.通过这个项目我真正的找到了开发安卓软件的感觉,从此逐渐步入安卓开发的正规.这个项目是当时借鉴Mars老师的初学视频做的安州手机的MP3播放器.自己又进行了改进,特别在歌词的优化和加入进度条方面的.因为是8个月前做的,水平非常0基础,bug应该非常多,如今自己又懒得再一次改进,仅仅希望可以给初学的朋友们提供一些帮助.或者起到抛砖引玉的效果.那我就心惬意足了. 先整体介绍

安卓MP3播放器开发实例(3)之进度条和歌词更新的实现

上一次谈了音乐播放的实现,这次说下最复杂的进度条和歌词更新.因为须要在播放的Activity和播放的Service间进行交互,所以就涉及了Activity对Service的绑定以及绑定后数据的传输,这个须要对服务绑定熟悉才干够理解.原理不复杂.可是步骤略微繁琐,代码贴起来可能会非常混乱. 进度条和歌词放在一起说比較好,不然比較混乱.进度条的调整大家都懂的,就是进度条调到哪里歌曲的播放就跟到哪里,歌词也得跟到哪里.首先看下上一篇看过的開始button监听事件中服务的绑定代码: //绑定播放服务 b

Android利用Filter过滤数据

MainActivity如下: package cc.testfilterable; import java.util.ArrayList; import java.util.HashMap; import android.os.Bundle; import android.widget.ListView; import android.app.Activity; import android.content.Context; /** * Demo描述: * 利用Filter过滤数据 * 可用于

Android练习项目 Mp3播放器实现 歌词同步播放(四)

其实到后面就需要我们如何显示歌词,对于歌词的同步显示还是比较好实现的,主要通过判断当前播放的时间和每个结点的歌词的时间的大小,来同步对应到结点的数据,现在就是如何来实现这个问题. 其实,这个时候就需要自定义控件来实现. 第一步需要自定义View的属性. 第二步需要实现在View的构造方法中获得我们自定义的属性. 主要通过初始化函数,init() 第三步,重写ondraw函数. LrcView.java package com.flashmusic.View; import android.con

Android利用数据库传送数据

---恢复内容开始--- 一.建表 //通过SQLiteDatabase 创建数据库stu.db3 final SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(this.getFilesDir().toString()+"/stu.db3", null); db.execSQL("CREATE TABLE student(id INTEGER PRIMARY KEY, name VARCHAR(50), scor

Android --------- 利用SharedPreferences存取数据

//向SharedPreferences中存放数据 //1.定义SharedPreferences对象,通过getSharedPreferences方法得到 SharedPreferences sp = getSharedPreferences(文件名, 模式);//模式通常为MODE_PRIVATE //2.获得该SharedPreferences对象的编辑器Editor Editor editor = sp.edit(); //3.通过编辑器向SharedPreferences中存放数据 e