本文介紹MediaPlayer的使用。MediaPlayer可以播放音頻和視頻,另外也可以通過VideoView來播放視頻,雖然VideoView比MediaPlayer簡單易用,但定制性不如用MediaPlayer,要視情況選擇了。MediaPlayer播放音頻比較簡單,但是要播放視頻就需要SurfaceView。SurfaceView比普通的自定義View更有繪圖上的優勢,它支持完全的OpenGL ES庫。
先貼出本文程序運行結果的截圖,下面是上一首、下一首、播放、停止、自動下一首、可用SeekBar來調進度:
布局文件:
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout 3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:orientation="vertical"
9 tools:context="net.bwie.mediaplayer.MainActivity">
10
11 <RelativeLayout 12 android:layout_width="match_parent"
13 android:layout_height="wrap_content">
14
15 <TextView 16 android:id="@+id/current_time_tv"
17 android:layout_width="wrap_content"
18 android:layout_height="wrap_content"
19 android:text="當前時間"/>
20
21 <TextView 22 android:id="@+id/total_time_tv"
23 android:layout_width="wrap_content"
24 android:layout_height="wrap_content"
25 android:layout_alignParentRight="true"
26 android:text="全部時間"/>
27
28 <SeekBar 29 android:id="@+id/seek_bar"
30 style="?android:progressBarStyleHorizontal"
31 android:layout_width="match_parent"
32 android:layout_height="50dp"
33 android:layout_toLeftOf="@id/total_time_tv"
34 android:layout_toRightOf="@id/current_time_tv"/>
35
36 </RelativeLayout>
37
38 <LinearLayout 39 android:layout_width="match_parent"
40 android:layout_height="wrap_content">
41
42 <Button 43 android:id="@+id/previous_btn"
44 android:layout_width="0dp"
45 android:layout_height="wrap_content"
46 android:layout_weight="1"
47 android:text="上一曲"/>
48
49 <Button 50 android:id="@+id/play_btn"
51 android:layout_width="0dp"
52 android:layout_height="wrap_content"
53 android:layout_weight="1"
54 android:text="播放/暫停"/>
55
56 <Button 57 android:id="@+id/next_btn"
58 android:layout_width="0dp"
59 android:layout_height="wrap_content"
60 android:layout_weight="1"
61 android:text="下一曲"/>
62
63 </LinearLayout>
64
65
66 <ListView 67 android:id="@+id/list_view"
68 android:layout_width="match_parent"
69 android:layout_height="match_parent"
70 android:divider="#f00"
71 android:dividerHeight="2dp"/>
72
73 </LinearLayout>
Bean類:
1 package net.bwie.mediaplayer.bean; 2
3 public class MediaInfo { 4
5 private long _id; 6 private String uri;// 路徑
7 private String title; 8 private String artist;// 藝術家
9
10 public MediaInfo(long _id, String uri, String title, String artist) { 11 this._id = _id; 12 this.uri = uri; 13 this.title = title; 14 this.artist = artist; 15 } 16
17 public long get_id() { 18 return _id; 19 } 20
21 public void set_id(long _id) { 22 this._id = _id; 23 } 24
25 public String getUri() { 26 return uri; 27 } 28
29 public void setUri(String uri) { 30 this.uri = uri; 31 } 32
33 public String getTitle() { 34 return title; 35 } 36
37 public void setTitle(String title) { 38 this.title = title; 39 } 40
41 public String getArtist() { 42 return artist; 43 } 44
45 public void setArtist(String artist) { 46 this.artist = artist; 47 } 48
49 @Override 50 public String toString() { 51 return "MediaInfo{" +
52 "_id=" + _id +
53 ", uri='" + uri + '\'' +
54 ", title='" + title + '\'' +
55 ", artist='" + artist + '\'' +
56 '}'; 57 } 58 }
Activity主頁面:
1 /**
2 * 1、查詢全部歌曲信息並展示(ContentProvider數據庫) 3 * 2、點擊對應歌曲播放/暫停 4 * 3、播放/暫停按鈕。上一首/下一首 5 * 注意:設置外部存儲權限 6 * build.gradle中targetSdkVersion 22 7 * <p> 8 * 進度條部分 9 * 1、進度條跟隨歌曲進度而變化 10 * 實現周期性改變進度條位置:1、Handler,2、TimerTask定時器 11 * 2、拖動進度條讓MediaPlayer播放對應位置的聲音 12 * SeekBar綁定監聽器 13 * MediaPlayer綁定播放完畢監聽器,實現自動播放下一曲 14 */
15 public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, View.OnClickListener, SeekBar.OnSeekBarChangeListener, MediaPlayer.OnCompletionListener { 16
17 protected ListView mListView; 18 protected Button mPreviousBtn; 19 protected Button mPlayBtn; 20 protected Button mNextBtn; 21 protected TextView mCurrentTimeTv; 22 protected TextView mTotalTimeTv; 23 protected SeekBar mSeekBar; 24 private List<MediaInfo> mMediaInfoList; 25
26 private MediaPlayer mMediaPlayer; 27
28 // 記錄當前播放歌曲的位置
29 private int mCurrentPosition; 30
31 private Handler mHandler = new Handler(new Handler.Callback() { 32 @Override 33 public boolean handleMessage(Message msg) { 34
35 // 展示給進度條和當前時間
36 int progress = mMediaPlayer.getCurrentPosition(); 37 mSeekBar.setProgress(progress); 38 mCurrentTimeTv.setText(parseTime(progress)); 39
40 // 繼續定時發送數據
41 updateProgress(); 42
43 return true; 44 } 45 }); 46
47 @Override 48 protected void onCreate(Bundle savedInstanceState) { 49 super.onCreate(savedInstanceState); 50 super.setContentView(R.layout.activity_main); 51 initView(); 52
53 mMediaInfoList = getDatas(); 54
55 ArrayAdapter adapter = new ArrayAdapter(this, 56 android.R.layout.simple_list_item_1, 57 mMediaInfoList); 58
59 mListView.setAdapter(adapter); 60 mListView.setOnItemClickListener(this); 61 } 62
63 private void initView() { 64 mListView = (ListView) findViewById(R.id.list_view); 65 mPreviousBtn = (Button) findViewById(R.id.previous_btn); 66 mPreviousBtn.setOnClickListener(MainActivity.this); 67 mPlayBtn = (Button) findViewById(R.id.play_btn); 68 mPlayBtn.setOnClickListener(MainActivity.this); 69 mNextBtn = (Button) findViewById(R.id.next_btn); 70 mNextBtn.setOnClickListener(MainActivity.this); 71 mCurrentTimeTv = (TextView) findViewById(R.id.current_time_tv); 72 mTotalTimeTv = (TextView) findViewById(R.id.total_time_tv); 73 mSeekBar = (SeekBar) findViewById(R.id.seek_bar); 74
75 // SeekBar綁定監聽器,監聽拖動到指定位置
76 mSeekBar.setOnSeekBarChangeListener(this); 77
78 } 79
80 // 獲取系統媒體數據庫中的音頻多媒體信息
81 private List<MediaInfo> getDatas() { 82 List<MediaInfo> list = new ArrayList<>(); 83
84 // 使用內容解析者訪問系統提供的數據庫
85 Cursor cursor = getContentResolver() 86 .query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 87 null, 88 null, 89 null, 90 MediaStore.Audio.Media.DEFAULT_SORT_ORDER);// 默認排序順序 91 // 如果游標讀取時還有下一個數據,讀取
92
93 int idIndex = cursor.getColumnIndex(MediaStore.Audio.Media._ID);//獲取列名對應的索引
94 int titleIndex = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);// 標題
95 int artistIndex = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);// 藝術家
96 int uriIndex = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);// 文件路徑
97 while (cursor.moveToNext()) { 98 // 根據索引值獲取對應列名中的數值
99 long _id = cursor.getLong(idIndex); 100 String title = cursor.getString(titleIndex); 101 String artist = cursor.getString(artistIndex); 102 String uri = cursor.getString(uriIndex); 103
104 MediaInfo mediaInfo = new MediaInfo(_id, uri, title, artist); 105
106 list.add(mediaInfo); 107 } 108
109 for (MediaInfo mediaInfo : list) { 110 Log.d("1507", "" + mediaInfo.toString()); 111 } 112 return list; 113 } 114
115 @Override 116 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 117 mCurrentPosition = position; 118
119 changeMusic(mCurrentPosition); 120 } 121
122 @Override 123 public void onClick(View view) { 124
125 if (view.getId() == R.id.previous_btn) {// 上一首
126 changeMusic(--mCurrentPosition); 127 } else if (view.getId() == R.id.play_btn) {// 播放/暫停 128
129 // 首次點擊播放按鈕,默認播放第0首
130 if (mMediaPlayer == null) { 131 changeMusic(0); 132 } else { 133 playOrPause(); 134 } 135
136 } else if (view.getId() == R.id.next_btn) {// 下一首
137 changeMusic(++mCurrentPosition); 138 } 139
140 } 141
142 // 播放或暫停
143 private void playOrPause() { 144 if (mMediaPlayer.isPlaying()) { 145 mMediaPlayer.pause(); 146 } else { 147 mMediaPlayer.start(); 148 } 149 } 150
151 // 基本數據類型和String在方法調用中傳遞的是值 152 // 其他數據類型在方法調用中傳遞的是引用 153
154 // 切歌
155 private void changeMusic(int position) { 156 if (position < 0) { 157 mCurrentPosition = position = mMediaInfoList.size() - 1; 158 } else if (position > mMediaInfoList.size() - 1) { 159 mCurrentPosition = position = 0; 160 } 161
162 if (mMediaPlayer == null) { 163 mMediaPlayer = new MediaPlayer(); 164 // 綁定播放完畢監聽器
165 mMediaPlayer.setOnCompletionListener(this); 166 } 167
168 try { 169 // 切歌之前先重置,釋放掉之前的資源
170 mMediaPlayer.reset(); 171 // 設置播放源
172 mMediaPlayer.setDataSource(mMediaInfoList.get(position).getUri()); 173 // 開始播放前的准備工作,加載多媒體資源,獲取相關信息
174 mMediaPlayer.prepare(); 175 // 開始播放
176 mMediaPlayer.start(); 177 } catch (IOException e) { 178 e.printStackTrace(); 179 Toast.makeText(this, "播放錯誤", Toast.LENGTH_SHORT).show(); 180 } 181
182 // 切歌時重置進度條並展示歌曲時長
183 mSeekBar.setProgress(0); 184 mSeekBar.setMax(mMediaPlayer.getDuration()); 185 mTotalTimeTv.setText(parseTime(mMediaPlayer.getDuration())); 186
187 updateProgress(); 188 } 189
190 // 每間隔1s通知更新進度
191 private void updateProgress() { 192 // 使用Handler每間隔1s發送一次空消息,通知進度條更新
193 Message msg = Message.obtain();// 獲取一個現成的消息
194 mHandler.sendMessageDelayed(msg, INTERNAL_TIME); 195 } 196
197 // 解析時間
198 private String parseTime(int oldTime) { 199 SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");// 時間格式
200 String newTime = sdf.format(new Date(oldTime)); 201 return newTime; 202 } 203
204 private static final int INTERNAL_TIME = 1000;// 音樂進度間隔時間
205
206 @Override 207 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 208
209 } 210
211 @Override 212 public void onStartTrackingTouch(SeekBar seekBar) { 213
214 } 215
216 // 當手停止拖拽進度條時執行該方法 217 // 獲取拖拽進度 218 // 將進度對應設置給MediaPlayer
219 @Override 220 public void onStopTrackingTouch(SeekBar seekBar) { 221 int progress = seekBar.getProgress(); 222 mMediaPlayer.seekTo(progress); 223 } 224
225 @Override 226 public void onCompletion(MediaPlayer mp) { 227 // 當歌曲播放完畢,切歌到下一首
228 changeMusic(++mCurrentPosition); 229 } 230 }
權限:
1 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>