一個簡易的音頻播放器實作
XML代碼塊
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<SeekBar
android:id="@+id/seek_bar"
android:max="100"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"/>
<LinearLayout
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="@+id/play_or_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"/>
<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止"/>
</LinearLayout>
</LinearLayout>
界面圖片

實作功能
- 可以通過拖動進度條來播放音樂,音樂播放時進度條也會跟著移動,當播放完畢時進度條走滿
- 點擊播放鍵進入播放狀態,同時按鍵文字變為“暫停”,點擊“暫停”時暫停播放,文字變為“播放”,下次再點擊“播放”將繼續從上次播放位置播放
- 在任意狀態點擊“停止”將會停止播放,下次播放從頭開始
定義好功能介面,能控制播放功能以及UI界面改變功能,
public interface IPlayControl {
//播放狀態
int PLAYER_STATE_PLAY=1;
int PLAYER_STATE_PAUSE=2;
int PLAYER_STATE_STOP=3;
/**
* 把UI的控制權介面設定給邏輯層
* @param iPlayViewControl
*/
void registerViewController(IPlayViewControl iPlayViewControl);
/**
* 取消介面的注冊
*/
void unregisterViewController();
/**
* 播放音樂
*/
void playOrPause();
/**
* 停止播放
*/
void stop();
/**
* 設定播放進度
* @param seek 這個是播放進度
*/
void seekTo(float seek);
}
/**
*這是用來控制UI的
*/
public interface IPlayViewControl {
/**
* 播放狀態改變通知
*/
void onPlayerStateChange(int state);
/**
* 進度條狀態改變通知
*/
void onSeekChange(float seek);
}
在Activity中
@RequiresApi(api = Build.VERSION_CODES.M)
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int PERMISSION_CODE = 1;
private SeekBar mSeekBar;
private Button mPlayOrPause;
private Button mStop;
private boolean mIsBind;
private IPlayControl mIPlayControl;
private boolean isUserTouchProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控制元件
initView();
//設定事件監聽
initListener();
//開啟服務(因為系結服務不能長期運行,所以采用二者結合的方式)
startService(new Intent(this, PlayService.class));
//因為要要在記憶體卡中讀取音樂,需要獲得運行時權限(模擬器api27,大于等于29的需要在
//AndroidManifest.xml中添加android:requestLegacyExternalStorage="true")
if ((checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED)) {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},PERMISSION_CODE);
}else {
//系結服務
initBindService();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==PERMISSION_CODE){
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
//系結服務
initBindService();
}
}
}
/**
* 系結服務(不能長時期運行)
*/
private void initBindService() {
Intent intent = new Intent(MainActivity.this, PlayService.class);
mIsBind = bindService(intent, mConnection, BIND_AUTO_CREATE);
Log.d(TAG, "bindService..");
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIPlayControl = (IPlayControl) service;
mIPlayControl.registerViewController(mIPlayViewControl);
Log.d(TAG,"mConnection..");
}
@Override
public void onServiceDisconnected(ComponentName name) {
//如果斷開連接就把它置空
mIPlayControl = null;
}
};
private void initListener() {
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//進度條發生改變
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
//用戶用手拖動進度條
isUserTouchProgress=true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//進度條停止拖動,獲得當前進度
int touchProgress = seekBar.getProgress();
Log.d(TAG, "touchProgress==" + touchProgress);
if (mIPlayControl != null) {
//呼叫這個方法可以用進度條控制音頻播放,傳入的是一個百分比
mIPlayControl.seekTo(touchProgress);
}
isUserTouchProgress=false;
}
});
mPlayOrPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//播放或暫停
if (mIPlayControl != null) {
mIPlayControl.playOrPause();
}
}
});
mStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//關閉按鈕被點擊了
if (mIPlayControl != null) {
mIPlayControl.stop();
}
}
});
}
private void initView() {
mSeekBar = this.findViewById(R.id.seek_bar);
mPlayOrPause = this.findViewById(R.id.play_or_pause);
mStop = this.findViewById(R.id.stop);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mIsBind && mConnection != null) {
Log.d(TAG, "onDestroy...");
//釋放資源
mIPlayControl.unregisterViewController();
unbindService(mConnection);
mIsBind = false;
}
}
/**
* UI控制介面應該實作在Activity中
*/
private IPlayViewControl mIPlayViewControl = new IPlayViewControl() {
@Override
public void onPlayerStateChange(int state) {
//跟據播放狀態來更新UI
switch (state){
case PLAYER_STATE_PLAY:
//播放狀態的話要修改按鈕顯示成暫停
mPlayOrPause.setText("暫停");
break;
case PLAYER_STATE_PAUSE:
case PLAYER_STATE_STOP:
//暫停狀態的話要修改按鈕顯示成播放
mPlayOrPause.setText("播放");
break;
}
}
@Override
public void onSeekChange(final float seek) {
//改變進度條更新,有一個條件,當用戶手碰在進度條上就不更新,不然會抖動
Log.d(TAG, "onSeekChange: -->"+seek);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!isUserTouchProgress) {
mSeekBar.setProgress((int)seek);
}
}
});
}
};
}
在Service中
/**
* 在相應的方法中設定好邏輯
*/
public class PlayService extends Service {
private PlayerPresenter mPlayerPresenter;
@Override
public void onCreate() {
super.onCreate();
if(mPlayerPresenter==null){
//當mPlayerPresenter為空時創建,這是繼承的Binder類
mPlayerPresenter=new PlayerPresenter();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//系結之后回傳一個IBinder型別
return mPlayerPresenter;
}
@Override
public void onDestroy() {
super.onDestroy();
//服務銷毀時將其置空
mPlayerPresenter=null;
}
}
在PlayerPresenter中
繼承了Binder類同時實作了IPlayContol介面,因為這個唯一能夠使Service跟Activity進行資料交換的橋梁,
首先實作方法
public class PlayerPresenter extends Binder implements IPlayControl {
private static final String TAG = "PlayerPresenter";
//設定成成員變數控制UI
private IPlayViewControl mIPlayViewControl;
//設定初始播放狀態為停止播放
private int currentState = PLAYER_STATE_STOP;
//定義一個播放器
public MediaPlayer mMediaPlayer;
//開啟一個計時器
private Timer mTimer;
//開啟一個任務
private SeekTimeTask mSeekTimeTaskask;
@Override
public void registerViewController(IPlayViewControl iPlayViewControl) {
//獲得UI控制權
this.mIPlayViewControl = iPlayViewControl;
}
@Override
public void unregisterViewController() {
mIPlayViewControl=null;
}
@Override
public void playOrPause() {
Log.d(TAG,"playOrPause");
if(currentState==PLAYER_STATE_STOP){
//創建播放器
initPlayer();
//設定資料源
try {
//按照流程,具體可以參考Android.cn搜索MediaPlayer
mMediaPlayer.setDataSource("/mnt/sdcard/jay.mp3");
mMediaPlayer.prepare();
mMediaPlayer.start();
//改變當前狀態
currentState=PLAYER_STATE_PLAY;
startTimer();
} catch (IOException e) {
e.printStackTrace();
}
}else if(currentState==PLAYER_STATE_PLAY){
//如果當前狀態是播放的我們就暫停
if (mMediaPlayer != null) {
mMediaPlayer.pause();
currentState=PLAYER_STATE_PAUSE;
stopTimer();
}
}else if(currentState==PLAYER_STATE_PAUSE){
//如果當前是暫停的我們就繼續播放音樂
if (mMediaPlayer != null) {
mMediaPlayer.start();
currentState=PLAYER_STATE_PLAY;
startTimer();
}
}
//通知UI界面
if (mIPlayViewControl != null) {
mIPlayViewControl.onPlayerStateChange(currentState);
}
}
/**
* 初始化播放器
*/
private void initPlayer() {
if (mMediaPlayer==null) {
mMediaPlayer = new MediaPlayer();
//設定播放型別
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
}
@Override
public void stop() {
Log.d(TAG,"stop");
//stop的時候要判斷是否有在播放
if (mMediaPlayer != null ) {
mMediaPlayer.stop();
//更新播放狀態和UI
currentState=PLAYER_STATE_STOP;
stopTimer();
if (mIPlayViewControl != null) {
mIPlayViewControl.onPlayerStateChange(currentState);
}
//釋放資源
mMediaPlayer.release();
mMediaPlayer=null;
}
}
@Override
public void seekTo(float seek) {
Log.d(TAG,"seekTo.."+seek);
//0~100之間,需要做一個轉換,得到的seek是一個百分比
if (mMediaPlayer != null) {
int tarSeek = (int) (seek*1.0f/100 * mMediaPlayer.getDuration());
mMediaPlayer.seekTo(tarSeek);
}
}
/**
* 開啟一個TimerTask
*/
private void startTimer(){
if (mTimer == null) {
mTimer = new Timer();
}
if (mSeekTimeTaskask == null) {
mSeekTimeTaskask = new SeekTimeTask();
}
//把任務扔到計時器里面,在特定的延遲時間每500ms更新一次
mTimer.schedule(mSeekTimeTaskask,0,500);
}
private void stopTimer(){
if (mTimer != null) {
mTimer.cancel();
mTimer=null;
}
if (mSeekTimeTaskask != null) {
mSeekTimeTaskask.cancel();
mSeekTimeTaskask=null;
}
}
//設定一個task放進Timer中,每隔一段時間進行一次
private class SeekTimeTask extends TimerTask {
@Override
public void run() {
if (mMediaPlayer != null && mIPlayViewControl!=null) {
//獲取當前的播放進度
int currentPosition = mMediaPlayer.getCurrentPosition();
//Log.d(TAG, "current play position --->"+ currentPosition);
//換算成占100里面的多少了(因為設定的進度條數值為100)
float curPosition = currentPosition*1.0f / mMediaPlayer.getDuration()*100;
Log.d(TAG,"curPosition--->"+curPosition);
//設定進度條顯示
mIPlayViewControl.onSeekChange(curPosition);
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/211943.html
標籤:其他
下一篇:Android WebView
