Android cung cấp lớp MediaPlayer
hỗ trợ play các file định dạng nhạc và phim… trong phần này chúng ta sẽ dùng lớp này đẻ xây dựng một ứng dụng nghe nhạc đơn giản.
Project
Chúng ta tạo project như bình thường. Sau đó trong thư mục res, chúng ta tạo một thư mục với tên là raw và đặt vào đó một file MP3 để sử dụng.
Layout
Bây giờ chúng ta thiết kế giao diện cho ứng dụng.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/songName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="songName"/> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/remainingTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="00:00"/> <SeekBar android:id="@+id/seekBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1"/> <TextView android:id="@+id/songDuration" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="00:00"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="30dp" android:gravity="center_horizontal" android:orientation="horizontal"> <Button android:id="@+id/btnPlay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="play" android:text="Play"/> <Button android:id="@+id/btnPause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="pause" android:text="Pause"/> <Button android:id="@+id/btnStop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="stop" android:text="Stop"/> </LinearLayout> </LinearLayout>
Ứng dụng sẽ có giao diện là 1 TextView
để hiển thị tên file nhạc, 2 TextView
để hiển thị thời gian của file nhạc và thời gian còn lại của file nhạc, 1 SeekBar
để biểu diễn thời lượng của nhạc, 3 Button
là Play, Pause và Stop.
Activity
Cuối cùng là lớp Activity.
package com.phocode; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.media.MediaPlayer; import android.view.View; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import java.util.concurrent.TimeUnit; public class MainActivity extends Activity implements OnSeekBarChangeListener { private MediaPlayer mediaPlayer; public TextView songName, songDuration, remainingTime; private Handler updateHandler = new Handler(); private SeekBar seekBar; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); songName = (TextView) findViewById(R.id.songName); songName.setText("gregg_lehrman_combust.mp3"); mediaPlayer = MediaPlayer.create(this, R.raw.gregg_lehrman_combust); songDuration = (TextView)findViewById(R.id.songDuration); int minutes = (int)TimeUnit.MILLISECONDS.toMinutes(mediaPlayer.getDuration()); int seconds = (int)TimeUnit.MILLISECONDS.toSeconds(mediaPlayer.getDuration()) - minutes * 60; songDuration.setText(String.format("%02d:%02d",minutes, seconds)); remainingTime = (TextView)findViewById(R.id.remainingTime); seekBar = (SeekBar)findViewById(R.id.seekBar); seekBar.setMax((int) mediaPlayer.getDuration()); seekBar.setOnSeekBarChangeListener(this); updateHandler = new Handler(); updateHandler.postDelayed(update, 100); } public void play(View view) { mediaPlayer.start(); } public void pause(View view) { mediaPlayer.pause(); } public void stop(View view) { mediaPlayer.stop(); mediaPlayer = mediaPlayer.create(this, R.raw.gregg_lehrman_combust); } private Runnable update = new Runnable() { public void run() { long currentTime = mediaPlayer.getCurrentPosition(); seekBar.setProgress((int)currentTime); int minutes = (int)TimeUnit.MILLISECONDS.toMinutes(currentTime); int seconds = (int)TimeUnit.MILLISECONDS.toSeconds(currentTime) - minutes * 60; remainingTime.setText(String.format("%02d:%02d",minutes, seconds)); updateHandler.postDelayed(this, 100); } }; @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if(fromUser) mediaPlayer.seekTo(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }
Lớp Activity
sẽ chịu trách nhiệm xử lý việc mở file, tạm dừng, đóng file và cập nhật thời lượng hiện tại của file nhạc.
mediaPlayer = mediaPlayer.create(this, R.raw.gregg_lehrman_combust);
Để tạo một đối tượng MediaPlayer
thì chúng ta dùng phương thức MediaPlayer.create(),
tham số thứ nhất là đối tượng Activity,
tham số thứ 2 là đối tượng file nhạc, ở đây chúng ta khai báo đường dẫn đến file nhạc nhưng không kèm theo đuôi .mp3.
songDuration = (TextView)findViewById(R.id.songDuration); int minutes = (int)TimeUnit.MILLISECONDS.toMinutes(mediaPlayer.getDuration()); int seconds = (int)TimeUnit.MILLISECONDS.toSeconds(mediaPlayer.getDuration()) - minutes * 60; songDuration.setText(String.format("%02d:%02d",minutes, seconds));
Bốn dòng code trên làm công việc lấy thời gian của file nhạc và gán vào TextView dưới dạng mm:ss (phút:giây).
seekBar = (SeekBar)findViewById(R.id.seekBar); seekBar.setMax((int) mediaPlayer.getDuration()); seekBar.setOnSeekBarChangeListener(this);
Tiếp theo chúng ta khởi tạo đối tượng SeekBar,
thiết lập thuộc tính max là thời lượng của file nhạc, sau đó gắn listener cho nó, phương thức getDuration()
sẽ trả về thời lượng nhạc theo mili giây và dữ liệu trả về là kiểu long
nên chúng ta ép qua kiểu
int
(tất nhiên bạn có thể dùng long
luôn cũng được).
updateHandler = new Handler(); updateHandler.postDelayed(update, 100);
Đáng tiếc là lớp MediaPlayer
không phát sinh sự kiện thay đổi thời lượng nhạc, vì thế nếu muốn biết thời lượng hiện tại là bao nhiêu thì chúng ta phải chạy tự gọi phương thức của lớp này để lấy dữ liệu, mà chúng ta lại muốn dùng dữ liệu đó để cập nhật lên SeekBar
thì phải chạy trong một luồng khác, ở đây chúng ta dùng lớp Handler
để làm việc này, cứ sau 1 giây thì chúng ta cập nhật lại SeekBar.
private Runnable update = new Runnable() { public void run() { long currentTime = mediaPlayer.getCurrentPosition(); seekBar.setProgress((int)currentTime); int minutes = (int)TimeUnit.MILLISECONDS.toMinutes(currentTime); int seconds = (int)TimeUnit.MILLISECONDS.toSeconds(currentTime) - minutes * 60; remainingTime.setText(String.format("%02d:%02d",minutes, seconds)); updateHandler.postDelayed(this, 100); } };
Lớp Handler
cho phép chúng ta thực thi các công việc bên trong đối tượng Runnable
hoặc Message trong các luồng khác mà không ảnh hưởng tới luồng chính. Ở đây chúng ta định nghĩa một đối tượng Runnable
tên là update,
đối tượng này sẽ làm công việc lấy thời lượng nhạc hiện tại và cập nhật lên SeekBar, đồng thời cập nhật lại thời gian đó trong các TextView
tương ứng.
public void play(View view) { mediaPlayer.start(); updateHandler.postDelayed(update, 100); }
Phương thức play()
sẽ được gọi khi người dùng click vào Button
“Play”. Tại đây chúng ta cho MediaPlayer
chạy file nhạc bằng cách gọi phương thức start().
public void pause(View view) { mediaPlayer.pause(); } public void stop(View view) { mediaPlayer.stop(); mediaPlayer = mediaPlayer.create(this, R.raw.gregg_lehrman_combust); }
Tương tự, muốn tạm dừng hoặc muốn dừng hẳn thì chúng ta gọi phương thức pause()
và phương thức stop()
tương ứng. Tuy nhiên sau khi gọi stop(),
nếu muốn mở lại nhạc thì chúng ta phải reset lại tất cả trạng thái của đối tượng MediaPlayer,
hoặc nhanh hơn là tạo lại một đối tượng MediaPlayer khác, như vậy mới có thể mở được.
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if(fromUser) mediaPlayer.seekTo(progress); }
Lớp MainActivity
của chúng ta có implement giao diện OnSeekBarChangeListener,
mục đích là để bắt sự kiện thay đổi tiến trình trên SeekBar,
bởi vì người dùng có thể dùng tay để điều khiển SeekBar
chứ không chỉ riêng đối tượng Handler,
nên ở đây chúng ta kiểm tra xem nếu người dùng kéo nút trên SeekBar
thì chúng ta cập nhật mới vị trí của nhạc.
Dịch và chạy project, chúng ta có được ứng dụng như hình dưới.
Bạn có thể kết hợp với trình duyệt file trong bài trước để nâng cấp ứng dụng lên cho hoàn thiện hơn.