3.1. Xây dựng kiến trúc thức về Java – Android
Xây dựng hàm MainActivity
Hình 3.1: Giao diện chính của ứng dụng
Giao diện chính của chương trình gồm các TextView và các ImageView. Các TextView tương ứng sẽ có từng chức năng riêng. Nếu click vào TextView thì ứng dụng sẽ chuyển sang fragment tương ứng với chức năng đó. Các TextView hiển thị số lượng về ca sĩ, thể loại, chủ đề là những số được đặt sẵn. Riêng TextView hiển thị số bài hát offline sẽ được tự động thay đổi nếu download thêm bài hát về.
Phía dưới cùng là một fragment gồm các chức năng chính của ứng dụng, có TextView hiển thị bài hát, các ImageView để có thể play, pause, next, previous, repeate.
- Layout_Play: Có các control của ứng dụng nghe nhạc như: Play, Pause, Next, Previous, SongName, Repeate.
- TextView CaSi: Khi click vào TextView CaSi thì sẽ sang fragment CaSi
- TextView ChuDe: Khi click vào TextView ChuDe thì sẽ sang fragment ChuDe - TextView TheLoai: Khi click vào TextView TheLoai thì sẽ sang fragment
TheLoai
- TextView DanhSach: Khi click vào TextView DanhSach thì sẽ sang fragment DanhSach
- TextView Album: Khi click vào TextView Album thì sẽ sang fragment Album - TextView ThuMuc: Khi click vào TextView ThuMuc thì sẽ sang fragment
ThuMuc
- TextView CaSi_Size: Hiển thị số ca sĩ có trong danh sách ca sĩ - TextView ChuDe_Size: Hiển thị số chủ đề có trong danh sách chủ đề - TextView TheLoai_Size: Hiển thị số thể loại có trong danh sách thể loại - TextView DanhSach_Size: Hiển thị số bài hát trong sdcard
Nguyên lý hoạt động của các thuộc tính trong Layout_Play:
- tvSongName: TextView hiển thị tên bài hát:
Hình 3.2: TextView hiển thị tên bài hát
Khi bài hát được phát (mp.isPlaying) thì tvShow sẽ cập nhật tên bài hát trong danh sách bài hát đã đọc được trong file XML hoặc trong danh sách offline.
- tvCurrentTime, tvTotalTime: TextView hiển thị thời gian của bài hát.
Hình 3.3: TextView tvChiTiet hiển thị tổng thời gian của bài hát
tvCurrentTime: Hiển thị vị trí thời gian của bài hát. TextView này sẽ được trả về từ lớp Playlist: tvCurrentTime.settext(pl.mp. getCurrentPosition()) và sẽ được cập nhật lại theo từng giây. Cách cập nhật được sử dụng trong hàm run() của TimeTask.
tvTotalTime: Hiển thị tổng thời gian của bài hát. TextView này sẽ được trả về từ lớp Playlist: tvTotalTime.settext(pl.mp.getDuration()) và sẽ được cập nhật lại theo từng giây. Cách cập nhật được sử dụng trong hàm run() của TimeTask.
- TextView CaSi: TextView, khi click vào sẽ hiển thị danh sách các ca sĩ đã được tạo trước.
Hình 3.4: Danh sách ca sĩ đã được tạo
Hiện tại đã có được 20 ca sĩ gồm: Đàm Vĩnh Hưng, Quang Lê, Quốc Đại, Noo Phước Thịnh, Văn Mai Hương, Cao Thái Sơn, Dương Ngọc Thái, Trọng Tấn, Khắc Việt, Bằng Kiều, Lệ Quyên, Đông Nhi, Đan Trường, Vy Oanh, Miu Lê, Lam Trường, Hồ Ngọc Hà, Tuấn Hưng, Cẩm Ly, Mạnh Quỳnh.
- btnPrevious: ImageButton, quay lại bài hát trước đó.
Khi click vào button này thì sẽ hát bài hát trước đó trong danh sách bài hát đã đọc được ở file XML.
- btnPlayPause: ImageButton, Play hoặc Pause bài hát. Có thể Play hoặc Pause bài hát.
Nếu bài hát đang hát (Play.mp.isPlaying()) thì ta sẽ chuyển hình ảnh của Button btnPlayPause thành hình được lưu trong R.drawable có tên ic_media_pause, nếu bài hát đang dừng thì ta chuyển hình ảnh của Button thành hình có tên ic_media_Play.
Hàm Play.PlayPause() như sau: Nếu bài hát đang được phát (mp.isPlaying()) thì ta sẽ dừng bài hát lại (mp.Pause()) và gán biến trạng thái danghat=0. Nếu không thì sẽ phát bài hát (mp.Start()) và chuyển biến trạng thái danghat=1. Hàm được để trong Try – Catch vì có thể gây lỗi nếu MediaPlayer chưa được gán thì mp.Pause hoặc mp.Start sẽ lỗi.
- btnRepeate: ImageButton, Lặp lại một bài đang hát.
Hình 3.5: Button lặp một bài hát và trộn bài hoạt động
Lặp lại một bài hát đang chạy và đổi hình ảnh của ImageButton này thành hình ảnh có tên img_btn_Repeat_pressed hoặc img_btn_repeat được lưu trong R.drawable, nếu chưa có bài hát nào chạy thì ImageButton này sẽ tự động quay lại mặc định là không lặp lại bài hát. Khi click tiếp thì sẽ chuyển sang chế độ trộn bài hát. Chế độ này có thể hát các bài hát trong danh sách không theo thứ tự.
Những hàm có trong lớp MainActivity:
- checkInternet(): Hàm kiểm tra trạng thái kết nối internet (3G, Wifi). - onBackPressed(): Hàm để bắt phím quay lại trên điện thoại.
- capnhat(): Hàm để bắt đầu sử dụng timer. - Xoatimer(): Hàm để xóa timer đang có.
- onCompletion(MediaPlayer): hàm này sẽ được thực hiện khi MediaPlayer thực hiện xong.
- Class MytimeTask: Dùng để cập nhật thời gian đang chạy của bài hát, seekBar theo thời gian định sẵn.
Hàm checkInternet():
Hàm này có chức năng là kiểm tra trạng thái kết nối internet của điện thoại. Nếu 3G hoặc Wifi chưa được bật lên thì hàm sẽ trả về một thông báo là điện thoại chưa được kết nối internet và sẽ thoát chương trình. Còn nếu đã bật 3G hoặc Wifi thì hàm này sẽ chấp nhận là đã kết nối đến internet.
public void checkInternet() {
if (isNetworkAvaiable()) {} else {
AlertDialog.Builder builder = new AlertDialog.Builder( MainActivity.this);
builder.setTitle("3G hoặc Wifi chưa được bật");
builder.setMessage("Kiểm tra lại tình trạng của 3G hoặc Wifi!");
builder.setPositiveButton("Finish",new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {
finish(); }});
builder.show(); }}
onBackPressed():
Hàm được thực hiện khi người sử dụng click vào nút quay lại trên điện thoại. Hàm sẽ hiển thị ra một thông báo là hỏi người dùng có muốn thoát chương trình hay không. Nếu có thì chọn “Thoát”, và không muốn thoát thì sẽ chọn “Không”.
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); alertDialogBuilder.setTitle("Thoát chương trình?");
alertDialogBuilder
.setMessage("Chọn 'THOÁT' để thoát chương trình!") .setCancelable(false)
.setPositiveButton("Thoát",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { moveTaskToBack(true);
startAppAd.onBackPressed();
Android.os.Process.killProcess(Android.os.Process.myPid()); System.exit(1); }})
.setNegativeButton("Không", new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int id) {dialog.cancel();}});
AlertDialog alertDialog = alertDialogBuilder.create(); alertDialog.show();
capnhat(): Khi bắt đầu chạy chương trình thì hàm này sẽ được chạy. Hàm có chức năng khởi tạo giá trị cho Timer là cứ sau mỗi giây lại chạy timer 1 lần.
public void capnhat() {
MyTimer1 = new Timer();
myti = new MytimeTask();
MyTimer1.scheduleAtFixedRate(myti, 1000, 1000);}
Xoatimer():Khi bắt đầu chạy chương trình thì hàm này sẽ được chạy. Hàm có chức năng là nếu timer đã được khởi tạo và đang chạy thì sẽ xóa timer đó đi và khởi tạo lại từ đầu.
public void Xoatimer() {
if (MyTimer1 != null) {
MyTimer1.cancel(); MyTimer1 = null;
}}
onCompletion(MediaPlayer): Hàm chỉ được thực hiện khi MediaPlayer thực hiện xong. Trong đề tài này thì hàm này sẽ được hát bài hát kế tiếp.
public void onCompletion(MediaPlayer mp1) { if (mp1.isLooping()) {
mp1.start();
} else {pl.Next();}}
Class MytimeTask: Lớp này là lớp thực hiện mỗi khi timer chạy. Hàm này sẽ cho phép cập nhật lại thời gian bài hát đang chạy, tổng thời gian của bài hát, tên bài hát, thanh seekbar theo thời gian của timer.
Class DownloadImageTask: Hàm này để download và setimage đã download hình ảnh có đường link là urls.
class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;}
protected Bitmap doInBackground(String... urls) { String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream(); mIcon11 = BitmapFactory.decodeStream(in); } catch (Exception e) { Log.e("Error", e.getMessage()); e.printStackTrace(); } return mIcon11;}
protectedvoid onPostExecute(Bitmap result) { bmImage.setImageBitmap(result);}}
3.2. Xây dựng hàm Playlist.java
Hình 3.6: Giao diện của Playlist_main.xml
Các thuộc tính:
- lvPlaylist: ListView, hiển thị danh sách bài hát. - Hàm Playlist.java có chức năng:
- Hiển thị danh sách bài hát lên lvPlaylist.
- -Nếu chọn một bài hát trong lvPlaylist thì có thể phát bài hát đó. Các hàm thực hiện trong lớp Playlist.class:
PlayPause():
Thực hiện Play hoặc Pause khi nhấn nút btnPlayPause bên lớp Mainactivity.class. Nếu phát hiện bài hát đang được hát thì sẽ dừng lại và gán biến danghat=0, còn nếu bài hát đang dừng thì phát bài hát và gán biến danghat=1;
public void PlayPause() { try {
if (mp.isPlaying()) { mp.pause();
danghat = 0; main.btnPlayPause.setImageResource(R.drawable.ic_media_play); } else {mp.start(); danghat = 1; main.btnPlayPause.setImageResource(R.drawable.ic_media_pause); }} catch (Exception ex) {}}
Next():Thực hiện hát bài hát tiếp theo trong danh sách bài hát khi nhấn nút
btnNext bên lớp Mainactivity.class. Đầu tiên phải gán biến danghat=0 vì khi hàm thực hiện thì cần một khoảng thời gian để cho MediaPlayer dừng làm việc. Nếu MediaPlayer đang phát thì sẽ dừng lại, kiểm tra nếu biến issuffle ở lớp MainActivity =1 thì sẽ thực hiện chọn Random, còn nếu biến issuffle =0 thì sẽ cộng thêm 1 ở biến VT và cho hát bài hát có vị trí là VT trong danh sách bài hát
public void Next() { danghat = 0; try { if (mp.isPlaying()) mp.stop(); } catch (Exception e) { } try { if (!main.issuffle) { if (VT < ListLink.size() - 1) { VT++;} else VT = 0; } else {
Random rand = new Random();
VT = rand.nextInt((ListLink.size() - 0 + 1)) + 0;} mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC); mp.setDataSource(ListLink.get(VT).trim()); main.tvTenBaiHat.setText("" + ListName.get(VT)); main.btnPlayPause.setImageResource(R.drawable.ic_media_pause); mp.prepare(); mp.start();}}
Previous():Thực hiện quay lại bài hát tiếp theo khi nhấn nút btnPrevious bên
lớp Mainactivity.class.
PlaySong(int ViTriBaiHat): Hàm thực hiện phát bài hát có vị trí là ViTriBaiHat
trong danh sách các bài hát.
public void PlaySong(int vitribaihat) throws IOException { try {
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC); mp.setDataSource(ListLink.get(vitribaihat).trim());
VT = vitribaihat;
WifiLock wifiLock = ((WifiManager) getSystemService (Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
mp.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
dialog = ProgressDialog.show(this, ", Loading...."); new MyTask().execute(); main.tvTenBaiHat.setText("" + ListName.get(vitribaihat)); main.btnPlayPause.setImageResource(R.drawable.ic_media_pause); danghat = 1; mp.setOnCompletionListener(new OnCompletionListener() { @Override
public void onCompletion(MediaPlayer mp) { if (mp.isLooping()) {
mp.start(); } else { Next();}}});
} catch (Exception ex) {}}
saxparser(final String a): Hàm cho phép đọc dữ liệu từ đường dẫn a để lấy tên
bài hát, tên ca sĩ, đường link bài hát, đường link hình ảnh ca sĩ. public void saxparser(final String a) {
try {
obj = new HandleXML(a); obj.fetchXML();
while (obj.parsingComplete);
progressDialog = ProgressDialog.show(Playlist.this, "","Loading..."); new Thread() {
public void run() { try {
sleep(3000);
} catch (Exception e) {
Log.e("tag", e.getMessage());} progressDialog.dismiss();
}}.start();} catch (Exception ex) {}}
Hàm repeate(): Hàm này xác định giá trị 0,1 và 2. Nếu biến Check_Repeate=0 thì
đang ở trạng thái mặc định: Không lặp 1 bài, không trộn bài.
Nếu Check_Repeate=1 thì đang ở trạng thái lặp 1 bài hát. Khi đó, chuyển hình ảnh của ImageRepeate và đặt lại giá trị khi hát xong bài hát đó.
Nếu Check_Repeate=2 thì đang ở trạng thái trộn bài. public static void repeate() {
if (check_Repeate < 2) { check_Repeate++; } else {
check_Repeate = 0; }
switch (check_Repeate) {
case 0: //No repeate, no shuffle
btnRepeat.setImageResource(R.mipmap.ic_player_repeat_off); mp.setLooping(false);
issuffle = false; break;
case 1://repeate, no shuffle mp.setLooping(true); issuffle = false;
btnRepeat.setImageResource(R.mipmap.ic_player_repeat_one); break;
case 2://no repeate, shuffle mp.setLooping(false); issuffle = true; btnRepeat.setImageResource(R.mipmap.ic_player_shuffle_off); break; } } 46
3.3. Xây dựng hàm ListCaSi.java
Hình 3.7: Giao diện của ListCaSi.xml
Danh sách ca sĩ gồm những ca sĩ sau: Đàm Vĩnh Hưng, Quang Lê, Quốc Đại, Noo Phước Thịnh, Văn Mai Hương, Cao Thái Sơn, Dương Ngọc Thái, Trọng Tấn, Khắc Việt, Bằng Kiều", Lệ Quyên, Đông Nhi, Đan Trường, Vy Oanh, Miu Lê, Lam Trường, Hồ Ngọc Hà, Tuấn Hưng, Cẩm Ly, Mạnh Quỳnh.
Những thông tin về các bài hát của các ca sĩ được lưu trong một file.xml trên internet. Trong bài này thì em dùng cách đọc SAX để đọc file.xml trên internet Khi đọc file.xml thì sẽ nhận được thông tin của bài hát như: tên bài hát, tên ca sĩ,
link bài hát, link hình ảnh của ca sĩ đó.
Danh sách các link của từng ca sĩ theo thứ tự:
"http://radio.zing.vn/xml/radio/knJGtZCTkbxyFGkn?seq=0", "http://radio.zing.vn/xml/radio/LGxHTJbcykFJtvmZm?seq=0", "http://radio.zing.vn/xml/radio/LmJmyNLXTLbcyvGkH?seq=0", "http://radio.zing.vn/xml/radio/kncHyRXhTZvctDHkH?seq=0", "http://radio.zing.vn/xml/radio/knJnysksbTZbxyFHZm?seq=0 ", "http://radio.zing.vn/xml/radio/ZGxHtvEtLvJyDmkm?seq=0", "http://radio.zing.vn/xml/radio/ZGxntsdmTkbcTFnZG?seq=0",
"http://radio.zing.vn/xml/radio/ZnxmTALxyZbxTbHLn?seq=0", "http://radio.zing.vn/xml/radio/ZHJGyvxHgTLDJybGLn?seq=0", "http://radio.zing.vn/xml/radio/LGxmtdngyZbJtFnZG?seq=0", "http://radio.zing.vn/xml/radio/ZHxGyLnmyLFJtFHLH?seq=0", “http://radio.zing.vn/xml/radio/LnxntAAlyLbJyFnLm?seq=0", "http://radio.zing.vn/xml/radio/kmxHyXCTLFcTDnZn?seq=0", "http://radio.zing.vn/xml/radio/ZHJGydZaDyZFJTDHLm?seq=0", "http://radio.zing.vn/xml/radio/ZnJmyAduhyLDxTbHZH?seq=0", "http://radio.zing.vn/xml/radio/LGJHyDLlyLFJyvHLn?seq=0", "http://radio.zing.vn/xml/radio/LmcmyAJATZDJyDnLm?seq=0", "http://radio.zing.vn/xml/radio/kGcHTcdbykvJyFGZn?seq=0", "http://radio.zing.vn/xml/radio/LmJnykLDyZvxyFHLm?seq=0", "http://radio.zing.vn/xml/radio/LmJmyzpkyLDcyDnLn?seq=0"
Lớp ListCaSi.Java chỉ có ListView lvDanhSachCaSi và những mảng String để lưu tên ca sĩ, đường link hình ảnh của ca sĩ đó.
Mỗi khi xảy ra sự kiên click trên ListView lvDanhSachCaSi thì sẽ trả về đường link theo thứ tự của ca sĩ và chuyển sang giao diện Playlist.xml
lvDanhSachCaSi = (ListView) findViewById(R.id.lvDanhSachCaSi); lvDanhSachCaSi.setAdapter(new CustomAdapter(this, DanhSachCaSi, DanhSachHinhAnh));
WifiLock wifiLock = ((WifiManager)getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
lvDanhSachCaSi.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int vitri, long arg3) {
Link = DanhSachCaSi[vitri];
Toast.makeText(getApplicationContext(), Link,Toast.LENGTH_SHORT).show(); 48
Intent i = new Intent(ListCaSi.this, Playlist.class); startActivity(i); MainActivity.imgView.setImageResource(DanhSachHinhAnh[vitri]); finish(); } }); 3.4. Xây dựng lớp ChuDe Hình 3.8. Hình ảnh về giao diện chủ đề
Giao diện chủ đề có 5 ImageView về 5 chủ đề khác nhau: Nhạc trẻ, Nhạc Việt, Nhạc cách mạng, Nhạc trữ tình, Nhạc mới. Khi click vào ImageView NhacTre hoặc các ImageView tương ứng thì ứng dụng sẽ chuyển sang fragment TheLoai. Trong fragment TheLoai này sẽ hiển thị ra danh sách các thể loại của chủ đề vừa chọn. Mỗi chủ đề lại có số lượng thể loại khác nhau. Khi click vào thể loại nào thì sẽ cho ra danh sách bài hát của thể loại đó. Để xác định được người dùng đã chọn chủ đề nào thì em đã sử dụng biến int ChuDe. Biến này sẽ được đánh dấu từ 1 đến 5. Nếu chọn ImgageView NhacTre thì biến ChuDe=1, chọn ImageView NhacViet thì biến ChuDe=2, chọn ImageView NhacCachMang thì biến ChuDe=3, chọn ImageView NhacTruTinh thì ChuDe=4, chọn ImageView NhacMoi thì ChuDe=5. Sau đó bên fragment sẽ switch(ChuDe) và case các trường hợp để hiển thị ra danh sách thể loại tương ứng. Trong danh sách thể loại em cũng có sử dụng thêm biến int TheLoai. Biến này thay đổi theo vị trí chọn trên ListView TheLoai.