SeekBar使用显示歌曲播放进度及时间
我们之前播放音乐的时候都会有进度条,今天我们就来加一个进度条,并显示你的播放进度和当前歌曲时间。
我们先看看效果吧万一不是你要的那个,不就浪费你的时间了,效果图如下:
(不可否认,丑是丑了点,但是有内涵,你懂得啊!)
接下来就来实现这个效果吧。
我们就不新建项目了,就用之前的那个MediaPlayerDemo吧,如果你是第一次看,可以点击最上方的链接去看前一篇文章。
1.修改activity_layout.xml
我们既然要加进度条和时间显示肯定是要先修改布局文件的,修改代码如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_weight="1"> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_marginLeft="10dp" android:layout_width="40dp" android:layout_height="wrap_content" android:id="@+id/tv_start" /> <SeekBar android:layout_width="270dp" android:layout_height="wrap_content" android:id="@+id/seekbar" /> <TextView android:layout_width="40dp" android:layout_height="wrap_content" android:id="@+id/tv_end" /> </LinearLayout> <LinearLayout android:gravity="bottom" android:layout_marginBottom="2dp" android:layout_gravity="bottom" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/play" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Play"/> <Button android:id="@+id/pause" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Pause"/> <Button android:id="@+id/stop" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Stop"/> </LinearLayout> </LinearLayout>
可以看到我们新增了两个LinearLayout(线性布局),一个留着备用,第二个里面我们放了两个TextView(用于显示时间)和一个SeekBar(进度条)。
我们想一下,我们已经知道这个音频文件放在手机里面了,也已经可以播放了,那么我要用进度条来显示当前歌曲的播放进度该怎么做,并且你可以通过手指拖拽这个Seekbar来到你想要的歌曲片段出,并且松手就要播放音乐·,还有就是怎么获取这个歌曲的时间呢?带着问题去想怎么实现会让你有种恍然大明白的感觉(你也别嫌我啰嗦啊,正所谓同是天涯程序员,相煎何太急啊!)。
protected SeekBar seekBar;//进度条 private Timer timer;//定时器 protected TextView tv_start;//开始时间 protected TextView tv_end;//结束时间 private boolean isSeekbarChaning;//互斥变量,防止进度条和定时器冲突。 private Button play;//播放按钮 private Button pause;//暂停按钮 private Button stop;//停止按钮
//绑定监听器,监听拖动到指定位置 seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { int duration2 = mediaPlayer.getDuration() / 1000;//获取音乐总时长 int position = mediaPlayer.getCurrentPosition();//获取当前播放的位置 tv_start.setText(calculateTime(position / 1000));//开始时间 tv_end.setText(calculateTime(duration2));//总时长 } @Override public void onStartTrackingTouch(SeekBar seekBar) { isSeekbarChaning = true; } @Override public void onStopTrackingTouch(SeekBar seekBar) { isSeekbarChaning = false; mediaPlayer.seekTo(seekBar.getProgress());//在当前位置播放 tv_start.setText(calculateTime(mediaPlayer.getCurrentPosition() / 1000)); } });
解释一下,首先我们定义了SeekBar,然后调用SeekBar的setOnSeekBarChangeListener()(PS:这个方法的意思是进图条改变时执行,无论是自己改变还是人为改变都会执行)方法。然后它里面会有是三个构造方法,分别是onProgressChanged()、onStartTrackingTouch()、onStopTrackingTouch()。刚看到这个你可能有点懵,解释一下,
**1.onProgressChanged()**这个方法我理解为进度条改变时使用的方法。这里面有三个参数,seekbar就是进度条,progress就是进度值,而fromUser参数,这个参数的作用是触发SeekBar的onProgressChanged回调接口时,可以根据这个参数判断是手动滑动SeekBar还是其他的一些方式改变了SeekBar的值。
2.onStartTrackingTouch通知用户已经开始一个触摸拖动手势。
**3.onStopTrackingTouch()**通知用户触摸手势已经结束。
现在应该好理解了吧。
还有就是计算播放时间的,代码如下:
//计算播放时间 public String calculateTime(int time) { int minute; int second; if (time >= 60) { minute = time / 60; second = time % 60; //分钟在0~9 if (minute < 10) { //判断秒 if (second < 10) { return "0" + minute + ":" + "0" + second; } else { return "0" + minute + ":" + second; } } else { //分钟大于10再判断秒 if (second < 10) { return minute + ":" + "0" + second; } else { return minute + ":" + second; } } } else { second = time; if (second >= 0 && second < 10) { return "00:" + "0" + second; } else { return "00:" + second; } } }
算法到是不难,需要理解一下,先定义分钟和秒,然后给一个时间判断,大于60的话就得出下面的分钟和秒,如果在0至9分钟之内,则判断具体多少秒。如果分钟大于10再判断秒。相信你看得懂。
然后我们看一下**initMediaPlayer()**里面要怎么添加这个时间,代码如下:
/* * 初始化MediaPlayer * */ private void initMediaPlayer(){ try { File file = new File(Environment.getExternalStorageDirectory(),"music.mp3"); mediaPlayer.setDataSource(file.getPath());//指定音频文件的路径 mediaPlayer.prepare();//让MediaPlayer进入到准备状态 }catch (Exception e){ e.printStackTrace(); } int duration2 = mediaPlayer.getDuration() / 1000; int position = mediaPlayer.getCurrentPosition(); tv_start.setText(calculateTime(position / 1000)); tv_end.setText(calculateTime(duration2)); }
通过定义一个两个值,一个播放时间,一个播放位置,开始时间通过刚才的算法得出赋值给tv_start显示在界面上。结束时间,通过计算赋值给定义的值,在赋值给tv_end显示在界面上。然后来看看**initView()**方法,我们的SeekBar的监听事件就是放在这个下面的,这个方法的完整代码如下:
/* * 初始化 * */ private void initView(){ tv_start = (TextView)findViewById(R.id.tv_start); tv_end = (TextView)findViewById(R.id.tv_end); seekBar = (SeekBar)findViewById(R.id.seekbar); //绑定监听器,监听拖动到指定位置 seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { int duration2 = mediaPlayer.getDuration() / 1000;//获取音乐总时长 int position = mediaPlayer.getCurrentPosition();//获取当前播放的位置 tv_start.setText(calculateTime(position / 1000));//开始时间 tv_end.setText(calculateTime(duration2));//总时长 } /* * 通知用户已经开始一个触摸拖动手势。 * */ @Override public void onStartTrackingTouch(SeekBar seekBar) { isSeekbarChaning = true; } /* * 当手停止拖动进度条时执行该方法 * 首先获取拖拽进度 * 将进度对应设置给MediaPlayer * */ @Override public void onStopTrackingTouch(SeekBar seekBar) { isSeekbarChaning = false; mediaPlayer.seekTo(seekBar.getProgress());//在当前位置播放 tv_start.setText(calculateTime(mediaPlayer.getCurrentPosition() / 1000)); } }); play.setOnClickListener(this); pause.setOnClickListener(this); stop.setOnClickListener(this); }
相信你都理解为什么这么做,最上面的就是我们开始时间、结束时间和进度条。最下面就是三个按钮的点击监听事件,为什么可以这样写,请看上一篇文章,我修改了一下onClick(),方法代码如下:
@Override public void onClick(View v){ switch (v.getId()){ case R.id.play: if(!mediaPlayer.isPlaying()){ mediaPlayer.start();//开始播放 int duration = mediaPlayer.getDuration();//获取音乐总时间 seekBar.setMax(duration);//将音乐总时间设置为Seekbar的最大值 timer = new Timer();//时间监听器 timer.schedule(new TimerTask() { @Override public void run() { if(!isSeekbarChaning){ seekBar.setProgress(mediaPlayer.getCurrentPosition()); } } },0,50); } break; case R.id.pause: if(mediaPlayer.isPlaying()){ mediaPlayer.pause();//暂停播放 } break; case R.id.stop: if(mediaPlayer.isPlaying()){ mediaPlayer.reset();//停止播放 initMediaPlayer(); } break; default: break; } }
主要的改动还是在启动播放里面主要是时间监听器,isSeekbarChaning为True时改变进度条。因为之前我们给它赋值为False,所以!isSeekbarChaning就为True。大致就是这样了,最后面,我放上MainActivity的所有代码,不然可能会被骂啊。
MainActivity.java完整代码如下:
package com.example.mediaplayerdemo; import android.Manifest; import android.content.pm.PackageManager; import android.media.MediaPlayer; import android.os.Environment; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import java.io.File; import java.util.Timer; import java.util.TimerTask; //继承View.OnClickListener,是按钮放在一起更直观,用另一种方法来设置按钮点击监听 public class MainActivity extends AppCompatActivity implements View.OnClickListener { //定义三个按钮并实例化MediaPlayer private MediaPlayer mediaPlayer = new MediaPlayer(); protected TextView tv_start; protected TextView tv_end; protected SeekBar seekBar; private Timer timer;//定时器 private boolean isSeekbarChaning;//互斥变量,防止进度条和定时器冲突。 private Button play; private Button pause; private Button stop; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); play = (Button)findViewById(R.id.play); pause = (Button)findViewById(R.id.pause); stop = (Button)findViewById(R.id.stop); play.setOnClickListener(this); pause.setOnClickListener(this); stop.setOnClickListener(this); if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this,new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE},1); }else { initView();//初始化进度条 initMediaPlayer();//初始化MediaPlayer } } /* * 初始化 * */ private void initView(){ tv_start = (TextView)findViewById(R.id.tv_start); tv_end = (TextView)findViewById(R.id.tv_end); seekBar = (SeekBar)findViewById(R.id.seekbar); //绑定监听器,监听拖动到指定位置 seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { int duration2 = mediaPlayer.getDuration() / 1000;//获取音乐总时长 int position = mediaPlayer.getCurrentPosition();//获取当前播放的位置 tv_start.setText(calculateTime(position / 1000));//开始时间 tv_end.setText(calculateTime(duration2));//总时长 } /* * 通知用户已经开始一个触摸拖动手势。 * */ @Override public void onStartTrackingTouch(SeekBar seekBar) { isSeekbarChaning = true; } /* * 当手停止拖动进度条时执行该方法 * 首先获取拖拽进度 * 将进度对应设置给MediaPlayer * */ @Override public void onStopTrackingTouch(SeekBar seekBar) { isSeekbarChaning = false; mediaPlayer.seekTo(seekBar.getProgress());//在当前位置播放 tv_start.setText(calculateTime(mediaPlayer.getCurrentPosition() / 1000)); } }); play.setOnClickListener(this); pause.setOnClickListener(this); stop.setOnClickListener(this); } //计算播放时间 public String calculateTime(int time) { int minute; int second; if (time >= 60) { minute = time / 60; second = time % 60; //分钟在0~9 if (minute < 10) { //判断秒 if (second < 10) { return "0" + minute + ":" + "0" + second; } else { return "0" + minute + ":" + second; } } else { //分钟大于10再判断秒 if (second < 10) { return minute + ":" + "0" + second; } else { return minute + ":" + second; } } } else { second = time; if (second >= 0 && second < 10) { return "00:" + "0" + second; } else { return "00:" + second; } } } /* * 初始化MediaPlayer * */ private void initMediaPlayer(){ try { File file = new File(Environment.getExternalStorageDirectory(),"music.mp3"); mediaPlayer.setDataSource(file.getPath());//指定音频文件的路径 mediaPlayer.prepare();//让MediaPlayer进入到准备状态 }catch (Exception e){ e.printStackTrace(); } int duration2 = mediaPlayer.getDuration() / 1000; int position = mediaPlayer.getCurrentPosition(); tv_start.setText(calculateTime(position / 1000)); tv_end.setText(calculateTime(duration2)); } //使用时弹出提示框 public void onRequestPermissionResult(int requestCode,String[] permissions,int[] grantResults){ switch (requestCode){ case 1: if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ initView(); initMediaPlayer(); }else { Toast.makeText(this,"拒绝权限将无法使用程序",Toast.LENGTH_SHORT).show(); finish(); } break; default: } } @Override public void onClick(View v){ switch (v.getId()){ case R.id.play: if(!mediaPlayer.isPlaying()){ mediaPlayer.start();//开始播放 int duration = mediaPlayer.getDuration();//获取音乐总时间 seekBar.setMax(duration);//将音乐总时间设置为Seekbar的最大值 timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { if(!isSeekbarChaning){ seekBar.setProgress(mediaPlayer.getCurrentPosition()); } } },0,50); } break; case R.id.pause: if(mediaPlayer.isPlaying()){ mediaPlayer.pause();//暂停播放 } break; case R.id.stop: if(mediaPlayer.isPlaying()){ mediaPlayer.reset();//停止播放 initMediaPlayer(); } break; default: break; } } @Override protected void onDestroy(){ super.onDestroy(); if(mediaPlayer != null){ mediaPlayer.stop(); mediaPlayer.release(); } } }