標籤:android mediaplayer listview cursor 多媒體處理
通過調用安卓的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卡的媒體資訊,我才省悟無須迭代這麼麻煩。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
【Android】利用安卓的資料介面、多媒體處理編寫記憶卡Mp3播放器app