先看一下效果图:
图片是可以旋转的,有进度条、计时时间、播放、暂停、退出等功能
界面布局
音频文件一般存放在res/raw文件夹,raw文件夹中的文件会被映射到R.java文件中,访问文件是直接使用资源id,如R.raw.music
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/music"
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:src="@drawable/music" />
<TextView
android:id="@+id/tvCurrent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/music"
android:layout_marginLeft="15dp"
android:text="00:00"
android:textSize="24sp" />
<TextView
android:id="@+id/tvFinal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/music"
android:layout_alignParentRight="true"
android:layout_marginRight="15dp"
android:text="00:00"
android:textSize="24sp" />
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tvCurrent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/seekbar"
android:layout_marginTop="20dp">
<Button
android:id="@+id/play"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="播放" />
<Button
android:id="@+id/pause"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="暂停播放" />
<Button
android:id="@+id/continuePlay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="继续播放" />
<Button
android:id="@+id/exit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="退出" />
</LinearLayout>
</RelativeLayout>
创建MusicService服务,音乐的加载、播放、暂停以及进度条的更新这些耗时操作,一般在Service中处理
package com.fangkuai.musicplayer;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import androidx.annotation.Nullable;
import java.util.Timer;
import java.util.TimerTask;
public class MusicService extends Service {
private MediaPlayer player;
private Timer timer;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MusicController();
}
@Override
public void onCreate() {
super.onCreate();
player=new MediaPlayer();
}
@Override
public void onDestroy() {
super.onDestroy();
if (player==null)return;
if (player.isPlaying())player.pause();
player.release();
player=null;
}
class MusicController extends Binder{
public void play(){
player.reset(); //重置MediaPlayer,从头播放
//导入音频资源
player=MediaPlayer.create(getApplicationContext(),R.raw.summertime);
player.start();
addTimer();
}
public void pause(){
player.pause();
}
public void continuePlayer(){
player.start();
}
public void seekTo(int progress){
player.seekTo(progress);
}
}
//添加计时器
private void addTimer() {
if (timer==null){
timer=new Timer();
TimerTask task=new TimerTask() {
@Override
public void run() {
//获取播放时长、播放位置
int duration=player.getDuration();
int currentposition=player.getCurrentPosition();
Message message=MainActivity.handler.obtainMessage();
//将消息封装在Bunndle中发送到消息队列
Bundle bundle=new Bundle();
bundle.putInt("duration",duration);
bundle.putInt("currentposition",currentposition);
message.setData(bundle);
MainActivity.handler.sendMessage(message);
}
};
//开启计时器,0.005s后开始,每0.5s执行一次
timer.schedule(task,5,500);
}
}
}
界面交互代码
package com.fangkuai.musicplayer;
import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements
View.OnClickListener {
private ImageView music;
private static TextView tvCurrent;
private static TextView tvFinal;
private static SeekBar seekbar;
private Button play;
private Button pause;
private Button continuePlay;
private Button exit;
private ObjectAnimator animator;
private MusicService.MusicController musicController;
private MyConn conn;
private Intent intent;
private boolean isUnbind=false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
//在主线程中处理子线程发来的消息
static Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
Bundle bundle=new Bundle();
bundle=msg.getData();
int duration=bundle.getInt("duration");
int currentposition=bundle.getInt("currentposition");
//给进度条设置总长度、当前位置进度
seekbar.setMax(duration);
seekbar.setProgress(currentposition);
//将时间转换成00:00的String格式添加到组件
int minute=duration/1000/60;
int second=duration/1000%60;
String strMinute=null;
String strSecond=null;
if (minute<10){
strMinute="0"+minute;
}else {
strMinute=minute+"";
} if (second<10){
strSecond="0"+second;
}else {
strSecond=second+"";
}
tvFinal.setText(strMinute+":"+strSecond);
minute=currentposition/1000/60;
second=currentposition/1000%60;
if (minute<10){
strMinute="0"+minute;
}else {
strMinute=minute+"";
} if (second<10){
strSecond="0"+second;
}else {
strSecond=second+"";
}
tvCurrent.setText(strMinute+":"+strSecond);
}
};
private void initView() {
music = (ImageView) findViewById(R.id.music);
tvCurrent = (TextView) findViewById(R.id.tvCurrent);
tvFinal = (TextView) findViewById(R.id.tvFinal);
seekbar = (SeekBar) findViewById(R.id.seekbar);
play = (Button) findViewById(R.id.play);
pause = (Button) findViewById(R.id.pause);
continuePlay = (Button) findViewById(R.id.continuePlay);
exit = (Button) findViewById(R.id.exit);
conn=new MyConn();
intent=new Intent(MainActivity.this, MusicService.class);
bindService(intent,conn,BIND_AUTO_CREATE);
seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
//滑动进度条时调用
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//滑动到末端,停止播放动画
if (progress==seekBar.getMax()){
animator.pause();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
//停止移动时触发
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//根据拖动进度条改变音乐播放进度
int progress=seekBar.getProgress();
musicController.seekTo(progress);
}
});
animator=ObjectAnimator.ofFloat(music,"rotation",0f,360.0f);
animator.setDuration(10000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(-1); //设置为无限循环
play.setOnClickListener(this);
pause.setOnClickListener(this);
continuePlay.setOnClickListener(this);
exit.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.play:
animator.start();
musicController.play();
break;
case R.id.pause:
animator.pause();
musicController.pause();
break;
case R.id.continuePlay:
animator.start();
musicController.continuePlayer();
break;
case R.id.exit:
unbind(isUnbind);
isUnbind=true;
finish();
break;
}
}
class MyConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
musicController= (MusicService.MusicController) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
private void unbind(boolean isUnbind){
if (!isUnbind){
musicController.pause();
unbindService(conn);
stopService(intent);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbind(isUnbind);
}
}