Wednesday, October 4, 2017

Nghe nhạc trực tuyến

Ta muốn play một file nhạc online. Trong lúc chờ tải sẽ hiện thông báo để người dùng biết và sau khi tải xong nhạc đã phát thì ẩn thông báo đi.
Ta sẽ dùng AsyncTask để làm việc này.
Tạo một class có tên tùy ý, ở đây là doc4, file xml như sau.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<TextView
 android:id="@+id/te"
android:layout_width="wrap_content"
 android:layout_height="wrap_content"
  android:layout_marginTop="45dp"
  android:layout_gravity="center_horizontal"
  android:text="Nghe nhạc trực tuyến"
  android:textSize="15sp" />

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="45dp"
android:orientation="horizontal" >

 <TextView
android:id="@+id/songDuration1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_weight="1" />

 <TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="left"
 android:layout_weight="1" />

 <TextView
  android:id="@+id/songDuration"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
  android:layout_gravity="right"
 android:layout_weight="1" />
 </LinearLayout>

 <SeekBar
 android:id="@+id/seekbar"
  android:layout_width="match_parent"
android:layout_height="wrap_content" />

 <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
 android:layout_marginTop="20dp"
 android:gravity="center_horizontal"
  android:orientation="horizontal" >
<ImageButton
   android:id="@+id/media_rew"
  android:layout_width="52sp"
  android:layout_height="35sp"
  android:background="@android:color/transparent"
  android:contentDescription="@null"
  android:onClick="rewind"
  android:src="@drawable/ic" />

  <ImageButton
    android:id="@+id/im"
    android:layout_width="52sp"
    android:layout_height="35sp"
    android:layout_marginLeft="14dp"
    android:background="@android:color/transparent"
    android:contentDescription="@null"
    android:src="@drawable/ic2" />

    <ImageButton
     android:id="@+id/media_ff"
     android:layout_width="52sp"
     android:layout_height="35sp"
     android:layout_marginLeft="14dp"
     android:background="@android:color/transparent"
     android:contentDescription="@null"
     android:onClick="forward"
     android:src="@drawable/ic3" />
  </LinearLayout>

</LinearLayout>
Copy các icon cần dùng cho nút play, pause và netx, previous vào thư mục drawable.

Copy các biến cần dùng lên trên Override.
private MediaPlayer mediaPlayer;
     String path;
private ImageButton buttonPlayPause;
private SeekBar seekBarProgress;
public TextView duration,duration2;
private double startTime = 0;
double timeElapsed = 0;
private double finalTime = 0;
private int forwardTime = 50000, backwardTime = 50000;
private int mediaFileLengthInMilliseconds;
private final Handler handler = new Handler();

Copy xuống dưới setContentView.
initView();
                  
path"https://s3.amazonaws.com/kargopolov/kukushka.mp3";
AsyncTask<Void, Void, Void> updateTask = new AsyncTask<Void, Void, Void>(){
ProgressDialog dialog = new ProgressDialog(doc4.this);
@Override
protected void onPreExecute() {
                 
     dialog.setMessage("Please wait.");
     dialog.setIndeterminate(true);
     dialog.setCancelable(false);
     dialog.show();
      }
     @Override
protected Void doInBackground(Void... params) {
      // do your background operation here
     try {
       mediaPlayer.setDataSource(path);   
      mediaPlayer.prepare();                   
     }
   catch (Exception e) {
     e.printStackTrace();
  } 
mediaFileLengthInMilliseconds = mediaPlayer.getDuration();
     return null;
     }
     @Override
protected void onPostExecute(Void result) {
// what to do when background task is completed
            
if(!mediaPlayer.isPlaying()){
     mediaPlayer.start();                   buttonPlayPause.setImageResource(R.drawable.ic2);         }
else {
     mediaPlayer.pause();
   buttonPlayPause.setImageResource(R.drawable.ic4);
}
finalTime = mediaPlayer.getDuration();
     timeElapsed = mediaPlayer.getCurrentPosition();
     primarySeekBarProgressUpdater();
     dialog.dismiss();
};
@Override
protected void onCancelled() {
     dialog.dismiss();
     super.onCancelled();
}
};
updateTask.execute((Void[])null);
Copy thêm vào khai báo class.
implements OnClickListener, OnTouchListener, OnCompletionListener, OnBufferingUpdateListener
im
Dấu lỗi đỏ kệ nó đã. Hãy xem code một chút. Ta tạo một Dialog, trong lúc chờ tải thì hiện thông báo để người dùng biết.
Trong AsyncTask ta tải nhạc, lấy độ dài bản nhạc, tải xong thì play, set các biểu tượng nút cho phù hợp, đồng thời tắt Dialog đi.
Copy ra ngoài setContentView.
private void initView() {
buttonPlayPause = (ImageButton)findViewById(R.id.im);
buttonPlayPause.setOnClickListener(this);
duration = (TextView) findViewById(R.id.songDuration);
duration2 = (TextView) findViewById(R.id.songDuration1);
seekBarProgress = (SeekBar)findViewById(R.id.seekbar);   
seekBarProgress.setMax(99); // It means 100% .0-99
seekBarProgress.setOnTouchListener(this);
                  
mediaPlayer = new MediaPlayer();     
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
seekBarProgress.setClickable(false);                     
}
Đây là khởi tạo cái seekbar là thanh báo mức nhạc đang phát đến đâu, có thể chạm tiến lùi để chọn thời điểm phát. Đồng thời khởi tạo biến mediaPlayer.
Copy tiếp xuống dưới hàm trên.
private void primarySeekBarProgressUpdater() {
      seekBarProgress.setProgress((int)(((float)mediaPlayer.getCurrentPosition()/mediaFileLengthInMilliseconds)*100)); // This math construction give a percentage of "was playing"/"song length"
         
if (mediaPlayer.isPlaying()) {
Runnable notification = new Runnable() {
     @TargetApi(Build.VERSION_CODES.GINGERBREAD)
     @SuppressLint("NewApi")
public void run() {              
                       
timeElapsed = mediaPlayer.getCurrentPosition();
double timeRemaining = finalTime - timeElapsed;
duration.setText(String.format("%d min, %d sec", TimeUnit.MILLISECONDS.toMinutes((long) timeRemaining), TimeUnit.MILLISECONDS.toSeconds((long) timeRemaining) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes((long) timeRemaining))));
startTime = mediaPlayer.getCurrentPosition();
                       
duration2.setText(String.format("%d min, %d sec",
     TimeUnit.MILLISECONDS.toMinutes((long) startTime),
     TimeUnit.MILLISECONDS.toSeconds((long) startTime) -
     TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.
     toMinutes((long) startTime)))
);
     primarySeekBarProgressUpdater();
}
};
                
 // handler.postDelayed(this,100);
                  handler.postDelayed(notification,1000);
}
}
Đây là hàm điều khiển cái seekbar, để nó chạy dần khi nhạc đang phát, hiện số thời gian lên các textView.
Copy tiếp xuống dưới.
@Override
     public void onClick(View v) {    
if(v.getId() == R.id.im){
/* ImageButton onClick event handler. Method which start/pause mediaplayer playing */
try {                  
mediaPlayer.setDataSource(path);                mediaPlayer.prepare();
} catch (Exception e) {
     e.printStackTrace();
}            
mediaFileLengthInMilliseconds = mediaPlayer.getDuration(); // gets the song length in milliseconds from URL
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();                 
buttonPlayPause.setImageResource(R.drawable.ic2);
                  
}else {
mediaPlayer.pause();
buttonPlayPause.setImageResource(R.drawable.ic4);
}            
finalTime = mediaPlayer.getDuration();
timeElapsed = mediaPlayer.getCurrentPosition();
primarySeekBarProgressUpdater();          
duration.setText(String.format("%d min, %d sec",
TimeUnit.MILLISECONDS.toMinutes((long) finalTime),
TimeUnit.MILLISECONDS.toSeconds((long) finalTime) -
                       TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.
                       toMinutes((long) finalTime)))
);
}
}
Đây là hàm điều khiển cái nút phát hoặc pause, thay đổi icon cho phù hợp trạng thái, lấy thời gian phát set ra textView.
Copy tiếp xuống dưới.
@SuppressLint("ClickableViewAccessibility")
     @Override
     public boolean onTouch(View v, MotionEvent event) {
Toast.makeText(doc4.this,"Vui lòng chờ", Toast.LENGTH_SHORT).show();
         
if(v.getId() == R.id.seekbar){
//** Seekbar onTouch event handler. Method which seeks MediaPlayer to seekBar primary progress position*//*
if(mediaPlayer.isPlaying()){
SeekBar sb = (SeekBar)v;
int playPositionInMillisecconds = (mediaFileLengthInMilliseconds / 100) * sb.getProgress();
mediaPlayer.seekTo(playPositionInMillisecconds);
}
}
return false;
}
Đây là hàm điều khiển khi người dùng co kéo cái seekbar tiến lùi, thì tìm đến đúng thời gian theo tỷ lệ để phát.
Copy tiếp xuống dưới.
public void forward(View view) {
Toast.makeText(doc4.this, "Vui lòng chờ",
                        Toast.LENGTH_SHORT).show();
 //check if we can go forward at forwardTime seconds before song endes
  if ((timeElapsed + forwardTime) <= finalTime) {
        timeElapsed = timeElapsed + forwardTime;

        //seek to the exact second of the track
        mediaPlayer.seekTo((int) timeElapsed);
     }
 }
public void rewind(View view) {
Toast.makeText(doc4.this,"Vui lòng chờ",
                        Toast.LENGTH_SHORT).show();
//check if we can go forward at forwardTime seconds before song endes
if ((timeElapsed - backwardTime) > 0) {
 timeElapsed = timeElapsed - backwardTime;
         
         //seek to the exact second of the track
 mediaPlayer.seekTo((int) timeElapsed);
     }
 }
Đây là hàm điều khiển 2 nút tiến và lùi một mức thời gian phát nào đó, ở đây để là 50 giây.
Copy tiếp xuống dưới.
@Override
public void onCompletion(MediaPlayer mp) {
/** MediaPlayer onCompletion event handler. Method which calls then song playing is complete*/            
buttonPlayPause.setImageResource(R.drawable.ic4);
}

@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
/** Method which updates the SeekBar secondary progress by current song loading from URL position*/
seekBarProgress.setSecondaryProgress(percent);
}
Bên trên là hàm điều khiển khi bản nhạc kết thúc thì set icon play để có thể ấn chơi lại. Bên dưới là hàm update cái seekbar.
Copy đoạn sau trên ngoặc đóng cuối cùng.
@Override
    public void onDestroy() {
        // TODO Auto-generated method stub
     super.onDestroy();
        //Log.d(DEBUG_TAG, "In onDestroy.");
     if(mediaPlayer != null) {
          mediaPlayer.stop();
          finish();
       }
    }
Đây là để khi người dùng thoát app thì dừng nhạc lại.
Nhập các thư viện cần dùng vào, có thể còn dấu đỏ thì nháy vào để thêm dòng này lên đầu.
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
Thêm permission internet vào trong file Manifest.xml.
Chạy ứng dụng, nếu có kết nối internet nhạc sẽ phát sau khi tải xong.



No comments:

Post a Comment