音樂播放器 EasyMusic (一)

EasyMusicandroid

 

一. 代碼獲取git

  github 上連接爲 https://github.com/VincentWYJ/EasyMusic, 感興趣的朋友能夠同步下來看, 歡迎提出寶貴的意見.github

1. clone數組

  進入連接後點擊下圖黑圈內按鈕獲取 git 地址, 而後經過命令將代碼克隆到本地, 好比存儲在 E 盤根目錄的命令:網絡

  cd E:/eclipse

  git clone https://github.com/VincentWYJ/EasyMusic.git, 標紅部分就是項目代碼 git 地址.ide

2. download函數

  直接點擊上圖紅圈內按鈕並根據提示下載代碼到指定文件夾.佈局

 

二. 應用簡介優化

  Easy Music 致力於打造一款簡單易用的音樂播放器, 目前實現了部分基本功能,大體狀況爲:

  1. android 4.4.2 + eclipse;

  2. 本地音樂檢索與播放;

  3. 界面頂部提供三個按鈕 Music, Album, Artist;

  4. 界面中間在應用啓動時默認顯示 Music 對應的所有歌曲信息列表, 點擊 Album 或 Artist 顯示全部專輯或歌手信息列表; 這裏仍是採用較傳統的作法--FrameLayout + Fragment (add or replace), 而目前主流的通常均利用 ViewPager 來裝載頁面以使其能夠滑動, TableLayout 來實現頂部標題一欄 (按鈕), 結合標題下的指示橫條三者一塊兒使用達到像網易新聞那樣的界面效果;

  5. 點擊 Music 列表中的項直接播放對應的音樂文件, 點擊 Album 或者 Artist 列表中的項進一步顯示對應的歌曲信息列表, 同理此時點擊某項可播放;

  6. 界面下方包含了三部分, 一爲播放進度顯示條, 還可拖動來調節播放位置; 二爲時間與歌名的顯示; 三爲上一首, 下一首, 播放/暫停按鈕;

  7. 若用戶刪除某個音樂文件且沒有從新獲取歌曲列表, 當其點擊該首歌時給出提出文件已刪提示並將該歌曲項從列表中移除;

  8. 爲了方便查找, 對列表中的項進行了升序排列;

  先來看一下界面, Music, Album, Artist 對應的信息列表分別以下左中右圖:

  

 

三. 項目分析

  因爲目前爲止佈局及按鈕點擊效果文件比較簡單, 因此接下來只對項目中關鍵的類或代碼進行分析.

1. 歌曲信息類 MusicInfo

  該類記錄了一首歌曲幾乎所有的信息, 經常使用的有歌曲名 mTitle, 專輯名 mAlbum, 歌手名 mArtist 及歌曲存放路徑 mPath.

  下面給出類構造函數和元素--歌曲名獲取函數, 其餘信息獲取函數可從同步的代碼文件中進行查看.

 1 public MusicInfo(Bundle bundle) {
 2     mId = bundle.getInt(MediaStore.Audio.Media._ID);
 3     mTitle = bundle.getString(MediaStore.Audio.Media.TITLE);
 4     mTitleKey = bundle.getString(MediaStore.Audio.Media.TITLE_KEY);
 5     mArtist = bundle.getString(MediaStore.Audio.Media.ARTIST);
 6     mArtistKey = bundle.getString(MediaStore.Audio.Media.ARTIST_KEY);
 7     mComposer = bundle.getString(MediaStore.Audio.Media.COMPOSER);
 8     mAlbum = bundle.getString(MediaStore.Audio.Media.ALBUM);
 9     mAlbumKey = bundle.getString(MediaStore.Audio.Media.ALBUM_KEY);
10     mDisplayName = bundle.getString(MediaStore.Audio.Media.DISPLAY_NAME);
11     mYear = bundle.getInt(MediaStore.Audio.Media.YEAR);
12     mMimeType = bundle.getString(MediaStore.Audio.Media.MIME_TYPE);
13     mPath = bundle.getString(MediaStore.Audio.Media.DATA);
14       
15     mArtistId = bundle.getInt(MediaStore.Audio.Media.ARTIST_ID);
16     mAlbumId = bundle.getInt(MediaStore.Audio.Media.ALBUM_ID);
17     mTrack = bundle.getInt(MediaStore.Audio.Media.TRACK);
18     mDuration = bundle.getInt(MediaStore.Audio.Media.DURATION);
19     mSize = bundle.getInt(MediaStore.Audio.Media.SIZE);
20     
21     isRingtone = bundle.getInt(MediaStore.Audio.Media.IS_RINGTONE) == 1;
22     isPodcast = bundle.getInt(MediaStore.Audio.Media.IS_PODCAST) == 1;
23     isAlarm = bundle.getInt(MediaStore.Audio.Media.IS_ALARM) == 1;
24     isMusic = bundle.getInt(MediaStore.Audio.Media.IS_MUSIC) == 1;
25     isNotification = bundle.getInt(MediaStore.Audio.Media.IS_NOTIFICATION) == 1;           
26 }
1 public String getTitle () {
2     return mTitle;
3 }

2. 歌曲獲取類 GetMusicInfoList

  類的定義只有兩個部分, 一是須要查詢的歌曲信息-- key 數組, 二是根據 key 數組從設備中讀取音樂文件信息並返回以 MusicInfo 對象爲元素的 List 對象. 相應的代碼以下:

 1 public static final String[] MUSIC_KEYS = new String[]{
 2     MediaStore.Audio.Media._ID,
 3     MediaStore.Audio.Media.TITLE,
 4     MediaStore.Audio.Media.TITLE_KEY,
 5     MediaStore.Audio.Media.ARTIST,
 6     MediaStore.Audio.Media.ARTIST_ID,
 7     MediaStore.Audio.Media.ARTIST_KEY,
 8     MediaStore.Audio.Media.COMPOSER,
 9     MediaStore.Audio.Media.ALBUM,
10     MediaStore.Audio.Media.ALBUM_ID,
11     MediaStore.Audio.Media.ALBUM_KEY,
12     MediaStore.Audio.Media.DISPLAY_NAME,
13     MediaStore.Audio.Media.DURATION,
14     MediaStore.Audio.Media.SIZE,
15     MediaStore.Audio.Media.YEAR,
16     MediaStore.Audio.Media.TRACK,
17     MediaStore.Audio.Media.IS_RINGTONE,
18     MediaStore.Audio.Media.IS_PODCAST,
19     MediaStore.Audio.Media.IS_ALARM,
20     MediaStore.Audio.Media.IS_MUSIC,
21     MediaStore.Audio.Media.IS_NOTIFICATION,
22     MediaStore.Audio.Media.MIME_TYPE,
23     MediaStore.Audio.Media.DATA  
24 };
25 
26 public static List<MusicInfo> getMusicList(Context context, String selection, String[] selectionArgs, String sortOrder) {
27     List<MusicInfo> audioList = new ArrayList<MusicInfo>();
28 
29     ContentResolver resolver = context.getContentResolver();
30     
31     Cursor cursor = resolver.query(
32         MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
33         MUSIC_KEYS,
34         selection,
35         selectionArgs,
36         sortOrder);
37 
38     for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
39         Bundle bundle = new Bundle (); 
40         
41         for (int i = 0; i < MUSIC_KEYS.length; i++) {
42             final String key = MUSIC_KEYS[i];
43             final int columnIndex = cursor.getColumnIndex(key);
44             final int type = cursor.getType(columnIndex);
45             switch (type) {
46                 case Cursor.FIELD_TYPE_BLOB:
47                     break;
48                 case Cursor.FIELD_TYPE_FLOAT:
49                     float floatValue = cursor.getFloat(columnIndex);
50                     bundle.putFloat(key, floatValue);
51                     break;
52                 case Cursor.FIELD_TYPE_INTEGER:
53                     int intValue = cursor.getInt(columnIndex);
54                     bundle.putInt(key, intValue);
55                     break;
56                 case Cursor.FIELD_TYPE_NULL:
57                     break;
58                 case Cursor.FIELD_TYPE_STRING:
59                     String strValue = cursor.getString(columnIndex);
60                     bundle.putString(key, strValue);
61                     break;
62             }
63         }
64         
65         MusicInfo audio = new MusicInfo(bundle);
66         audioList.add(audio);
67     }
68     
69     cursor.close();
70     
71     return audioList;
72 }

  獲取的歌曲類型及信息能夠根據實際需求進行調整, 如類型能夠利用過濾機制只讀取 mp3 文件, 信息能夠只讀取上面提到的歌曲名等必要的四個或者五個, 這樣在音樂文件不少的狀況下能夠減小查詢時間也方便在播放過程當中找到目標歌曲.

  方法 getMusicList() 除返回最終 List<MusicInfo> 外, 有兩點須要注意:

  2.1 方法 query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 的五個參數:

    2.1.1 uri --須要查詢的資源類型, 音樂或音頻通常爲 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

    2.1.2 protection --須要獲取的信息 key 數組, 如上面定義的 MUSIC_KEYS;

    2.1.3 selection --信息選擇條件 (或者過濾機制條件), 若設置爲 null 則表示但願獲取 uri 對應的全部文件;

    2.1.4 selectionArgs --當須要爲 selecton 傳遞額外參數值時使用, 不須要則設置爲null;

    2.1.5 sortOrder --給獲取的結果信息列表設置排序機制;

  詳細的用法及參數設置會在後面被調用時給出.

  2.2 for 循環在將 cursor 中的信息寫入 bundle 對象前進行了不一樣類型的判斷, 只獲取了 Integer, Int, String三種類型的值.

  如今能夠依靠上面定義的兩個類來獲取設備中的音樂文件信息了, 接下來須要作的就是調用那些方法並把得到的結果進行處理後顯示在界面上, 還有就是音樂播放的控制. 第二部分應用簡介中提到界面佈局中間用於顯示歌曲信息列表, 從給出的三張效果圖也能夠看出, 中間部分顯示的內容改變時, 頂部和底部的佈局組件實際上是沒有從新繪製的. 中間佈局使用了 FrameLayout, 而 Music, Album, Artist 三個按鈕點擊後分別會生成三個 Fragment 對象 (固然確定是繼承它的子類), 而後利用 FragmentManeger 類的方法將其放置在 FrameLayout 中.

3. 主類 EasyMusicMainActivity

  做爲核心控制類要完成的事情不少, 主要有如下6點. 像按鈕響應這些常規的實現就囉嗦了, 只對關鍵的部分給出代碼講解.

  3.1 頂部三個按鈕的點擊響應;

  3.2 中間佈局內容的切換, 這取決於上一步的操做;

  3.3 中間信息列表項的點擊響應;

  3.4 文件從其餘途徑被刪除後, 點擊列表中對應項的處理;

  3.5 底部播放信息的顯示與更新;

  3.6 底部播放控制按鈕的點擊響應;

 1 public static List<MusicInfo> musicInfos = null;
 2 public static List<Map<String, Object>>musicMapList = null;
 3 public static SimpleAdapter musicInfoListAdapter = null;
 4     
 5 public static void getMusicInfos(String selection, String[] selectionArgs, String sortOrder){
 6     musicInfos = GetMusicInfoList.getMusicList(mContext, selection, selectionArgs, sortOrder);
 7     
 8     if(isGetMusicListFlag){
 9         musicMapList.clear();
10 
11         for(int i=0;i<musicInfos.size();++i){
12             Map<String, Object> map = new HashMap<String, Object>();
13             map.put("title", musicInfos.get(i).getTitle());
14             map.put("artist", musicInfos.get(i).getArtist());
15             map.put("album", musicInfos.get(i).getAlbum());
16             float duration = (float) (musicInfos.get(i).getDuration()/60.0/1000.0);
17             int pre = (int)duration;
18             float suf = (duration-pre)*60;
19             map.put("duration",String.valueOf(pre)+":"+decimalFormat.format(suf));
20             musicMapList.add(map);
21         }
22     }
23 }
24 
25 public static void getMusicInfoListAdapter(){
26     musicInfoListAdapter = new SimpleAdapter(mContext, musicMapList, R.layout.musicinfo_layout,
27         new String[]{"title", "artist", "duration"},
28         new int[]{R.id.left_top, R.id.left_bottom, R.id.right});
29 }

  像音樂信息列表這種數據操做通常都會用 ListView 組件來進行加載, 而數據加載須要藉助 BaseAdapter 的子類對象, 後者又須要利用 List 等存儲數據的類對象來進行初始化. 注意方法 getMusicInfoListAdapter() 中初始化 musicInfoListAdapter 對象時用的佈局文件是自定義的 R.layout.musicinfo_layout, 由於這裏涉及到了三個元素, 而 android 自身提供的佈局文件多爲一元或者二元. R.layout.musicinfo_layout 內容以下, 格局爲左側上下各一個元素, 右側居中一個元素.

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent" 
 4     android:layout_height="wrap_content"
 5     android:orientation="horizontal"
 6     android:gravity="center_vertical" >
 7     
 8     <LinearLayout
 9         android:layout_width="0dp" 
10         android:layout_height="wrap_content"
11         android:layout_weight="1"
12         android:layout_marginTop="5dp"
13         android:layout_marginBottom="5dp"
14         android:layout_marginStart="5dp"
15         android:orientation="vertical"
16         android:gravity="start|center" >
17         
18         <TextView android:id="@+id/left_top" 
19             style="@style/InfoTopTextView" />
20         
21         <TextView android:id="@+id/left_bottom" 
22             style="@style/InfoBottomTextView" />
23         
24     </LinearLayout>
25     
26     <TextView android:id="@+id/right" 
27         android:layout_width="wrap_content"
28         android:layout_height="match_parent"
29         android:layout_marginEnd="5dp"
30         android:textSize="@dimen/music_info_leftbottom_size"
31         android:gravity="center" />
32 
33 </LinearLayout>

  對照上面第一張效果圖--歌曲信息列表來看, 三個元素分別顯示了歌曲名, 歌手名, 歌曲時長. 然後兩張則分別顯示了專輯名及歌曲數和歌手名及歌曲數.

  這兩個方法放在主類中, 而不是單獨放到 Music 對應的 Fragment 中, 是由於除了 Music 模塊要獲取歌曲信息外, 在點擊了 Album 或者 Artist 對應的 Fragment 信息列表中的項時也須要獲取歌曲信息.

 1 switch (localMusicType) {
 2 case R.id.local_music_title:
 3     listFragment = new LocalMusicListFragment();
 4     break;
 5 case R.id.local_album_title:
 6     listFragment = new LocalAlbumListFragment();
 7     break;
 8 case R.id.local_artist_title:
 9     listFragment = new LocalArtistListFragment();
10     break;
11 default:
12     break;
13 }
14     
15 FragmentTransaction fTransaction = getFragmentManager().beginTransaction();
16 fTransaction.add(R.id.musicinfo_list_fragment, listFragment);
17 fTransaction.setTransition(FragmentTransaction.TRANSIT_NONE);
18 fTransaction.commit();

  這段代碼做爲按鈕 Music, Album, Artist 點擊響應的部分代碼, 用於切換中間佈局的 Fragment 內容, 三種類別分別對應LocalMusicListFragment, LocalAlbumListFragment, LocalArtistListFragment.

 1 public void setSeekBarOnClickListener(){
 2     musicPlaySeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
 3 
 4         @Override
 5         public void onStopTrackingTouch(SeekBar seekBar) {
 6             if(mediaPlayer != null){
 7                 mediaPlayer.seekTo(seekBar.getProgress());
 8             }
 9         }
10         
11         @Override
12         public void onStartTrackingTouch(SeekBar seekBar) {
13         }
14         
15         @Override
16         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
17             float duration = progress/60.0f/1000.0f;
18             int pre = (int)duration;
19             int suf = (int)((duration-pre)*60);
20             musicTimePlay.setText(String.valueOf(pre)+":"+decimalFormat.format(suf));
21         }
22     });
23 }

24 public static DecimalFormat decimalFormat = new DecimalFormat("00");

  這段代碼給播放進度組件 musicPlayseekBar 設置位置改變監聽器, 主要作了兩件事情:

  3.7 手動調節其指示點位置時更新音頻播放類對象 mediaPlayer 的播放位置;

  3.8 指示點變化時更新已播放時間組件 musicTimePlay 的顯示內容, 利用格式對象 decimalFormat 來統一秒數老是以兩位顯示;

 1 public void setSeekBarMoveListener(){
 2     new Thread(new Runnable() {
 3         
 4         @Override
 5         public void run() {
 6             while (true) {
 7                 try {
 8                     Thread.sleep(500);
 9                     if(isMusicPlaying){
10                         musicPlaySeekBar.setProgress(mediaPlayer.getCurrentPosition());
11                     }
12                 } catch (InterruptedException e) {
13                     e.printStackTrace();
14                 }
15             }
16         }
17     }).start();
18 }

  很遺憾 MediaPlayer 類除了能夠監聽當前音頻文件播放完畢意外, 沒有再提供其餘的監聽器了 (好比監聽播放進度). 因此只能採用例如上面代碼模塊那樣線程+定時的方式來使進度條指示器位置的不斷更新, 至於多久更新一次就看實際需求了, 間隔越小越流暢. 注意若以播放位置爲進度條的目標位置, 在初始化進度條最大值時也必須爲音頻大小 (毫秒爲單位的 int 型數值).

 1 public void MusicPlayControl(View playView){
 2     int id = playView.getId();
 3     switch (id) {
 4     case R.id.music_play_next:
 5         MusicPlay((positionPlay+1)%musicInfos.size());
 6         break;
 7     case R.id.music_play_pre:
 8         MusicPlay((musicInfos.size()+positionPlay-1)%musicInfos.size());
 9         break;
10     case R.id.music_play_pause:
11         if(isMusicPlaying){
12             isMusicPlaying = false;
13             mediaPlayer.pause();
14             musicPlayPause.setBackgroundResource(R.drawable.music_to_pause);
15         }else{
16             if(mediaPlayer != null){
17                 isMusicPlaying = true;
18                 mediaPlayer.start();
19                 musicPlayPause.setBackgroundResource(R.drawable.music_to_start);
20             }else{
21                 MusicPlay(positionPlay);
22             }
23         }
24         break;
25     default:
26         break;
27     }
28 }
29 
30 public static void MusicPlay(int position){
31     isMusicPlaying = false;
32     int totalTime = musicInfos.get(position).getDuration();
33     positionPlay = position;
34     musicPlaySeekBar.setMax(totalTime);
35     songPath = musicInfos.get(position).getPath();
36     File songFile = new File(songPath);
37     if(!songFile.exists()){
38         Toast.makeText(mContext, "The music file doesn't exists, already updated music list.", Toast.LENGTH_SHORT).show();
39         //only remove the special file
40         musicInfos.remove(position);
41         musicMapList.remove(position);
42         musicInfoListAdapter.notifyDataSetChanged();
43         return;
44     }
45     uri = Uri.fromFile(songFile);
46     try {
47         if(mediaPlayer != null){
48             mediaPlayer.stop();
49             mediaPlayer.release();
50             mediaPlayer = null;
51         }
52         mediaPlayer = new MediaPlayer();
53         mediaPlayer.setDataSource(mContext, uri);
54         mediaPlayer.prepare();
55         mediaPlayer.start();
56         isMusicPlaying = true;
57         setMusicViewInfos();
58     } catch (IllegalStateException e) {
59         e.printStackTrace();
60     } catch (IOException e) {
61         e.printStackTrace();
62     }
63 }

  方法 MusicPlayControl() 做爲按鈕上一首, 下一首, 暫停/播放的響應, 主要藉助控制方法 MusicPlay() 來進行相應的操做. 怎麼對點擊的音樂進行播放很簡單, 這裏須要注意的是代碼中的那一段 if(...){...}, 即若是某歌曲文件在列表生成後被其餘操做刪除了, 這裏能夠判斷出來給出文件刪除提示並馬上進行更新列表, 不然用戶體驗會很很差, 雖然從新啓動應用或者從新獲取列表也能夠達到更新的目的.

  注意若是隻是Adapter對應的數據源對應中的某項數據改變了, 只須要調用 notifyDataSetChanged() 方法便可讓 ListView 組件進行顯示內容的更新.

 1 public class SetElementComparator extends Collator
 2 {
 3     @Override
 4     public int compare(String s1, String s2)
 5     {
 6         return s1.compareTo(s2);
 7     }
 8 
 9     @Override
10     public CollationKey getCollationKey(String source)
11     {
12         return null;
13     }
14 
15     @Override
16     public int hashCode()
17     {
18         return 0;
19     }
20 }

  前面說過獲取的歌曲信息是升序排列的, 可是對於 Album 和 Artist 這兩種類別, 當一個專輯或者一個歌手有多首歌時, 是不應讓列表中出現多個相同專輯項的. 剛開始很粗心採用 Set<String> 對象來存儲專輯名或者歌手名, 雖然結果列表中元素重複是沒有了, 但會發現本來該升序的信息卻亂了. 查了資料才發現原來集合類 Set 在調用 add() 方法添加元素時是根據其 hashCode 值來決定元素位置的, 因此改用 TreeSet.  可是 TreeSet 好像對中文也不是 "敏感" 的, 解決辦法是自定義繼承 Collator 的類, 在建立實例時傳入 Locale.China 參數, 而後將實例對象傳入 TreeSet<String> 實例方法.

4. 專輯類 LocalAlbumListFragment

 1 public static Collator collator = SetElementComparator.getInstance(Locale.CHINA);  //定義在主類EasyMusicMainActivity中  2     
 3 public void getAlbumInfos(String selection, String[] selectionArgs, String sortOrder){
 4     EasyMusicMainActivity.getMusicInfos(selection, selectionArgs, sortOrder);
 5     
 6     albumMapList.clear();
 7     
 8     Set<String>albumNameSet = new TreeSet<String>(EasyMusicMainActivity.collator);
 9     for(int i=0; i<EasyMusicMainActivity.musicInfos.size(); ++i){
10         albumName = EasyMusicMainActivity.musicInfos.get(i).getAlbum();
11         albumNameSet.add(albumName);
12     }
13     
14     int albumCountArray[] = new int[albumNameSet.size()];
15     int index = 0;
16     for(Iterator<String>iter = albumNameSet.iterator(); iter.hasNext();){
17         String albumNameInSet = iter.next();
18         String albumArtist = null;
19         for(int i=0; i<EasyMusicMainActivity.musicInfos.size(); ++i){
20             albumName = EasyMusicMainActivity.musicInfos.get(i).getAlbum();
21             if(albumNameInSet.equals(albumName)){
22                 albumCountArray[index] += 1;
23                 albumArtist = EasyMusicMainActivity.musicInfos.get(i).getArtist();
24             }
25         }
26         
27         Map<String, Object> map = new HashMap<String, Object>();
28         map.put("album", albumNameInSet);
29         map.put("count", albumCountArray[index]+" 首 - "+albumArtist);
30         albumMapList.add(map);
31         ++index;
32     }
33 }
34 
35 public void getAlbumInfoListAdapter(){
36     EasyMusicMainActivity.musicInfoListAdapter = new SimpleAdapter(getActivity(), albumMapList, R.layout.musicinfo_layout,
37             new String[]{"album", "count"},
38             new int[]{R.id.left_top, R.id.left_bottom});
39 }

  獲取 Album 列表信息和歌曲不一樣的是, 須要去除重複並統計每張專輯的歌曲數, Adapter 對象初始化只傳入專輯名和歌曲數目, 佈局右側的欄位不顯示任何信息.

 1     musicInfoList.setAdapter(EasyMusicMainActivity.musicInfoListAdapter);
 2     musicInfoList.setOnItemClickListener(new OnItemClickListener() {
 3         
 4         @Override
 5         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 6             // TODO Auto-generated method stub
 7             EasyMusicMainActivity.isGetMusicListFlag = true;
 8             albumName = (String) albumMapList.get(position).get("album");
 9             EasyMusicMainActivity.getMusicInfos(MediaStore.Audio.Media.ALBUM+"=?", new String[]{albumName}, EasyMusicMainActivity.musicSortOrder);
10             EasyMusicMainActivity.getMusicInfoListAdapter();
11             musicInfoList.setAdapter(EasyMusicMainActivity.musicInfoListAdapter);
12             musicInfoList.setOnItemClickListener(new OnItemClickListener() {
13                 
14                 @Override
15                 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
16                     // TODO Auto-generated method stub
17                     EasyMusicMainActivity.MusicPlay(position);
18                 }
19             });
20         }
21     });
22 }

  信息列表項的點擊響應也會有所不一樣, 點擊專輯名後會須要獲取專輯對應的全部歌曲, 而後纔是最終的歌曲信息列表. 這裏在獲取歌曲時傳入了 selection 和 selectionArgs參數.

5. 歌手類 LocalArtistListFragment

  Artist 類別也是一樣的實現方式, 代碼以下.

 1 public void getArtistInfos(String selection, String[] selectionArgs, String sortOrder){
 2     EasyMusicMainActivity.getMusicInfos(selection, selectionArgs, sortOrder);
 3     
 4     artistMapList.clear();
 5     
 6     Set<String>artistNameSet = new TreeSet<String>(EasyMusicMainActivity.collator);
 7     for(int i=0; i<EasyMusicMainActivity.musicInfos.size(); ++i){
 8         artistName = EasyMusicMainActivity.musicInfos.get(i).getArtist();
 9         artistNameSet.add(artistName);
10     }
11     
12     int artistCountArray[] = new int[artistNameSet.size()];
13     int index = 0;
14     for(Iterator<String>iter = artistNameSet.iterator(); iter.hasNext();){
15         String artistNameInSet = iter.next();
16         for(int i=0; i<EasyMusicMainActivity.musicInfos.size(); ++i){
17             artistName = EasyMusicMainActivity.musicInfos.get(i).getArtist();
18             if(artistNameInSet.equals(artistName)){
19                 artistCountArray[index] += 1;
20             }
21         }
22         
23         Map<String, Object> map = new HashMap<String, Object>();
24         map.put("artist", artistNameInSet);
25         map.put("count", artistCountArray[index]+" 首 ");
26         artistMapList.add(map);
27         ++index;
28     }
29 }
30 
31 public void getArtistInfoListAdapter(){
32     EasyMusicMainActivity.musicInfoListAdapter = new SimpleAdapter(getActivity(), artistMapList, R.layout.musicinfo_layout,
33             new String[]{"artist", "count"},
34             new int[]{R.id.left_top, R.id.left_bottom});
35 }
36 
37 public void initArtistListView(){
38     musicInfoList.setAdapter(EasyMusicMainActivity.musicInfoListAdapter);
39     musicInfoList.setOnItemClickListener(new OnItemClickListener() {
40         
41         @Override
42         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
43             // TODO Auto-generated method stub
44             EasyMusicMainActivity.isGetMusicListFlag = true;
45             artistName = (String) artistMapList.get(position).get("artist");
46             EasyMusicMainActivity.getMusicInfos(MediaStore.Audio.Media.ARTIST+"=?", new String[]{artistName}, EasyMusicMainActivity.musicSortOrder);
47             EasyMusicMainActivity.getMusicInfoListAdapter();
48             musicInfoList.setAdapter(EasyMusicMainActivity.musicInfoListAdapter);
49             musicInfoList.setOnItemClickListener(new OnItemClickListener() {
50                 
51                 @Override
52                 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
53                     // TODO Auto-generated method stub
54                     EasyMusicMainActivity.MusicPlay(position);
55                 }
56             });
57         }
58     });
59 }

6. 歌曲類 LocalMusicListFragment

  Music 類別最簡單, 獲取的結果就是最終列表信息.

 1 public void initListView(){
 2     musicInfoList.setAdapter(EasyMusicMainActivity.musicInfoListAdapter);
 3     musicInfoList.setOnItemClickListener(new OnItemClickListener() {
 4         
 5         @Override
 6         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 7             // TODO Auto-generated method stub
 8             EasyMusicMainActivity.MusicPlay(position);
 9         }
10     });
11 }

 

四. 總結與討論

  目前該應用只是實現了一些最基本的功能, 還有不少事情及細節須要處理, 如應用界面不可見, 轉屏, 其餘應用須要音頻焦點, 網絡音頻文件的獲取/播放, 界面設計的優化佈局組件的調整等等. 歡迎感興趣或者有經驗的朋友可以一塊兒交流, 感謝.

相關文章
相關標籤/搜索