You are on page 1of 13

HƯỚNG DẪN XÂY DỰNG ỨNG DỤNG NGHE NHẠC

DÀNH CHO ANDROID


Trong phiên hướng dẫn này chúng ta sẽ xây dựng một chương trình chơi nhạc đơn giản dành cho
Android. Ứng dụng thu được như là kết quả của phiên làm việc sẽ có giao diện như sau:

Các bạn sẽ dựa vào các bước mô tả trong tài liệu hướng dẫn này để từng bước xây dựng ứng dụng. Mục
tiêu là các bạn có thể tạo ra được sản phẩm đầu tay, sau đó các bạn có thể tự xây dựng các ứng dụng
khác với quy mô tương tự.
Phần 1: Thiết kế giao diện
1. Khởi tạo dự án mới với tên là Zplayer

2. Chuẩn bị các tài nguyên ảnh và nhạc

Tải các file tài nguyên về tại đường dẫn sau: https://github.com/codegym-vn/android-
zplayer/raw/master/Resources.zip

Tiếp theo chúng ta sẽ thêm các tài nguyên cần thiết vào ứng dụng của mình bằng cách sao chép các thư
mục Songs chứa các bài hát mẫu vào thư mục assets trong dự án. Sao chép các hình ảnh trong thư mục
Images vào thư mục drawable của ứng dụng.
Ứng dụng của chúng ta chỉ có một giao diện chính, do đó, việc trước tiên chúng ta làm sẽ là thiết kế giao
diện cơ bản cho nó.

3. Tạo LinearLayout để chứa các thành phần giao diện


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="@android:color/black"
android:layout_height="match_parent"
tools:context="zplayer.gymcode.zplayer.MainActivity">

<LinearLayout
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>

4. Thêm Textview để hiển thị tên trang chính ứng dụng

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="@android:color/black"
android:layout_height="match_parent"
tools:context="zplayer.gymcode.zplayer.MainActivity">

<LinearLayout
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ZPLayer"
android:textColor="@android:color/white"
android:textSize="46sp" />
</LinearLayout>
</LinearLayout>

5. Thêm ImageView để hiển thị ảnh đại diện bài hát

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="@android:color/black"
android:layout_height="match_parent"
tools:context="zplayer.gymcode.zplayer.MainActivity">

<LinearLayout
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ZPLayer"
android:textColor="@android:color/white"
android:textSize="46sp" />

<ImageView
android:id="@+id/ivThumbnail"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:layout_marginTop="12dp"
android:src="@drawable/tcs" />
</LinearLayout>
</LinearLayout>

6. Thêm 2 textview để hiển thị tên bài hát và ca sĩ trình bày


<ImageView
android:id="@+id/ivThumbnail"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:layout_marginTop="12dp"
android:src="@drawable/tcs" />

<TextView
android:id="@+id/tvTitle"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="16sp"
android:text="Như tiếng thở dài"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvPerformer"
android:textColor="@android:color/white"
android:text="Trịnh Công Sơn"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

7. Thêm seekbar để hiển thị thời gian đã phát của bản nhạc. Và Relative chứa 2 textview để hiển
thị thời gian đã phát và thời gian còn lại

<SeekBar
android:id="@+id/seekBar"
android:layout_marginTop="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvCurrent"
android:layout_width="wrap_content"
android:textColor="@android:color/white"
android:layout_height="wrap_content"
android:text="00:08"/>

<TextView
android:id="@+id/tvRemaining"
android:textColor="@android:color/white"
android:layout_alignParentEnd="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="04:55"/>
</RelativeLayout>

8. Thêm Relative để chứa các nút play, next và Previous

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/ivPre"
android:layout_centerInParent="true"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_toLeftOf="@+id/ivPlay"
android:src="@drawable/prev"/>
<ImageView
android:id="@+id/ivPlay"
android:layout_centerInParent="true"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:src="@drawable/pause"/>
<ImageView
android:id="@+id/ivNext"
android:layout_toRightOf="@+id/ivPlay"
android:layout_centerInParent="true"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/next"/>
</RelativeLayout>

9. Thành quả sẽ được giao diện như sau


Phần 2: Viết mã
1. Đối tượng Song
Hãy tạo một lớp mới có tên là ZSong để lưu thông tin của một bài hát. Lớp này sẽ được đặt
trong thư mục model.

package zplayer.gymcode.zplayer.model;

public class ZSong {


private String Title;
private String Source;
private String Performer;
private int Thumbnail;

public String getTitle() {


return Title;
}

public void setTitle(String title) {


Title = title;
}

public String getPerformer() {


return Performer;
}

public void setPerformer(String performer) {


Performer = performer;
}

public int getThumbnail() {


return Thumbnail;
}

public void setThumbnail(int thumbnail) {


Thumbnail = thumbnail;
}

public String getSource() {


return Source;
}

public void setSource(String source) {


Source = source;
}
}

2. Đối tượng SongManager


Tạo lớp SongManager cũng trong thư mục trên. Đây là lớp chứa dữ liệu chính cho chương
trình, trong trường hợp này là danh sách các bản nhạc.

package zplayer.gymcode.zplayer;

import java.util.ArrayList;
import java.util.List;
import zplayer.gymcode.zplayer.model.ZSong;

public class SongManager {


private List<ZSong> songs;

public SongManager() {
songs = new ArrayList<>();
}

public List<ZSong> getSongs() {


return songs;
}

public void loadSongs() {


ZSong song = new ZSong();
song.setPerformer("Trịnh Công sơn");
song.setTitle("Như tiếng thở dài");
song.setSource("Songs/Nhu Tieng Tho Dai - Trinh Cong Son.mp3");
song.setThumbnail(R.drawable.tcs);
songs.add(song);
song = new ZSong();
song.setPerformer("Trịnh Công sơn");
song.setTitle("Tôi ơi đừng tuyệt vọng");
song.setSource("Songs/Toi Oi Dung Tuyet Vong - Trinh Cong Son.mp3");
song.setThumbnail(R.drawable.tcs2);
songs.add(song);
}
}

3. Viết mã cho MainActivity


Mở lớp MainActivity, trong hàm onCreate tìm đến đối tượng của view theo id như sau

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvCurrent = findViewById(R.id.tvCurrent);
tvRemaining = findViewById(R.id.tvRemaining);
tvTitle = findViewById(R.id.tvTitle);
tvPerformer = findViewById(R.id.tvPerformer);
ivThumbnail = findViewById(R.id.ivThumbnail);
seekBar = findViewById(R.id.seekBar);
ivPlay = findViewById(R.id.ivPlay);
ivNext = findViewById(R.id.ivNext);
ivPre = findViewById(R.id.ivPre);
}

4. Lấy về danh sách các bài hát


Khởi tạo tiếp lớp SongManager và gọi phương thức getSongs() sau đó lưu danh sách bài hát vào
thuộc tính songs của lớp MainActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvCurrent = findViewById(R.id.tvCurrent);
tvRemaining = findViewById(R.id.tvRemaining);
tvTitle = findViewById(R.id.tvTitle);
tvPerformer = findViewById(R.id.tvPerformer);
ivThumbnail = findViewById(R.id.ivThumbnail);
seekBar = findViewById(R.id.seekBar);
ivPlay = findViewById(R.id.ivPlay);
ivNext = findViewById(R.id.ivNext);
ivPre = findViewById(R.id.ivPre);

songManager = new SongManager();


songManager.loadSongs();
songs = songManager.getSongs();
}

5. Cập nhật thông tin bài hát lên giao diện


Viết hàm updateViewBySong để cập nhật bài hát hiện tại lên giao diện

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvCurrent = findViewById(R.id.tvCurrent);
tvRemaining = findViewById(R.id.tvRemaining);
tvTitle = findViewById(R.id.tvTitle);
tvPerformer = findViewById(R.id.tvPerformer);
ivThumbnail = findViewById(R.id.ivThumbnail);
seekBar = findViewById(R.id.seekBar);
ivPlay = findViewById(R.id.ivPlay);
ivNext = findViewById(R.id.ivNext);
ivPre = findViewById(R.id.ivPre);

songManager = new SongManager();


songManager.loadSongs();
songs = songManager.getSongs();
updateViewBySong(songs.get(index));
}

/**
* Cập nhật giao diện theo thông tin bản nhac
* @param song bản nhạc
*/
private void updateViewBySong(ZSong song) {
//Cập nhật thông tin trên view
tvTitle.setText(song.getTitle());
tvPerformer.setText(song.getPerformer());
ivThumbnail.setImageResource(song.getThumbnail());
try {
//Lấy thông tin thời gian bản nhạc
AssetFileDescriptor descriptor = getAssets().openFd(song.getSource());
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(descriptor.getFileDescriptor(),
descriptor.getStartOffset(), descriptor.getLength());
String duration =

metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
tvRemaining.setText(getTimeFormatByLong(Long.parseLong(duration)));
tvCurrent.setText("00:00");
} catch (IOException e) {
e.printStackTrace();
}
}

6. Thiết lập các sự kiện khi click vào các nút Play, Next, Pre

ivPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
tvCurrent.setVisibility(View.VISIBLE);
tvRemaining.setVisibility(View.VISIBLE);
if (m == null) {
playMusicBySong(songs.get(index));
return;
}
if (m.isPlaying()) {
ivPlay.setImageResource(R.drawable.play);
pauseMusic();
} else {
ivPlay.setImageResource(R.drawable.pause);
startMusic();
}
}
});
ivNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (index < songs.size() - 1) {
index++;
} else {
index = 0;
}
playMusicBySong(songs.get(index));
}
});
ivPre.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (index > 0) {
index--;
} else {
index = songs.size() - 1;
}
playMusicBySong(songs.get(index));
}
});

7. Cập nhật giao diện


Viết hàm PlayMusicBySong để thực hiện cập nhật giao diện theo bản nhạc và chơi bản nhạc
được chọn

/**
* Hàm chơi bản nhạc
* @param song bản nhạc cần chơi
*/
public void playMusicBySong(ZSong song) {
try {
updateViewBySong(song);
if (m == null) {
m = new MediaPlayer();
}
stopMusic();
AssetFileDescriptor descriptor = getAssets().openFd(song.getSource());
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(descriptor.getFileDescriptor(),
descriptor.getStartOffset(), descriptor.getLength());
m.setDataSource(descriptor.getFileDescriptor(),
descriptor.getStartOffset(), descriptor.getLength());
descriptor.close();
m.prepare();
m.setLooping(true);
m.start();
String duration =

metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
songDuration = Long.parseLong(duration);
seekBar.setMax((int) songDuration);
if (countDown != null) {
countDown.cancel();
}
startCountDownTimer(songDuration);
} catch (Exception e) {
e.printStackTrace();
}
}

8. Các sự kiện chơi nhạc, dừng và tạm dừng

/**
* Hàm dừng chơi nhạc
*/
private void stopMusic() {
if (m.isPlaying()) {
m.stop();
m.release();
m = new MediaPlayer();
}
}

/**
* Hàm bắt đầu chơi nhạc lại
*/
private void startMusic() {
m.start();
startCountDownTimer(currentUntilFinished);
}

/**
* Hàm tạm dừng chơi nhạc
*/
private void pauseMusic() {
m.pause();
countDown.cancel();
}

9. Viết hàm format định dạng thời gian theo millisecond

/**
* Hàm trả về định dạng time format theo phút:giây
* @param millis millis cần format
* @return chuỗi đã được format
*/
private String getTimeFormatByLong(long millis) {
String out;
long seconds = (millis % 60000) / 1000;
long minutes = (millis / 60000);
out = String.format("%02d:%02d", minutes, seconds);
return out;
}

10. Cập nhật thời gian phát bài hát


Viết hàm startCountDownTimer để thực hiện việc cập nhật thời gian phát và còn lại của bài hát

/**
* Khởi tạo bộ đếm countdown để thực hiện việc cập nhật thời gian phát và còn
lại của bài hát
* @param millisInFuture
*/
private void startCountDownTimer(long millisInFuture) {
countDown = new CountDownTimer(millisInFuture, 1000) {

public void onTick(long millisUntilFinished) {


currentUntilFinished = millisUntilFinished;
tvRemaining.setText(getTimeFormatByLong(millisUntilFinished));
tvCurrent.setText(getTimeFormatByLong((songDuration -
millisUntilFinished)));
seekBar.setProgress((int) (songDuration - millisUntilFinished));

public void onFinish() {


}

}.start();
}

11. Sản phẩm cuối


Mã nguồn ứng dụng mẫu có thể tải về tại đây: https://github.com/codegym-vn/android-zplayer

You might also like