最近在做仿网易云音乐播放器的实训项目,学习完后写此博客,一方面巩固自己理解,一方面方便各位浏览。
读完本文你将了解到:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
//设置背景图片
<ImageView
android:id="@+id/listen_background_iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/img1" />
<RelativeLayout
android:id="@+id/listen_rl"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_weight="1">
//设置返回按钮图片
<ImageView
android:id="@+id/listen_back1_iv"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:src="@mipmap/back1" />
//设置歌曲名
<TextView
android:id="@+id/listen_title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="8dp"
android:layout_toRightOf="@id/listen_back1_iv"
android:text="成都"
android:textColor="#f9f7f7"
android:textSize="20sp" />
//设置歌手名
<TextView
android:id="@+id/listen_artist_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/listen_title_tv"
android:layout_below="@id/listen_title_tv"
android:layout_marginTop="5dp"
android:text="赵雷-"
android:textColor="#aeabab"
android:textSize="15sp" />
//设置专辑名
<TextView
android:id="@+id/listen_album_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/listen_artist_tv"
android:layout_toRightOf="@id/listen_artist_tv"
android:text="成都"
android:textColor="#aeabab"
android:textSize="15sp" />
//设置“分享”按钮(本代码中未实现)
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginRight="15dp"
android:src="@mipmap/share" />
//设置布局间的线
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_below="@id/listen_artist_tv"
android:background="#bababa" />
RelativeLayout>
//设置唱片圆盘
<ImageView
android:id="@+id/listen_changpian_img"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@mipmap/play_page_disc" />
//设置圆盘上的指针
<ImageView
android:id="@+id/listen_zhizhen_iv"
android:layout_width="100dp"
android:layout_height="150dp"
android:layout_below="@id/listen_rl"
android:layout_centerHorizontal="true"
android:src="@mipmap/play_page_needle" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="15dp">
<RelativeLayout
android:id="@+id/listen_play"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="0dp">
//设置“暂停”
<ImageView
android:id="@+id/listen_pause1_img"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_marginTop="10dp"
android:src="@mipmap/icon_music_circle_pause" />
//设置“上一首”
<ImageView
android:id="@+id/listen_back_img"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_toLeftOf="@id/listen_pause1_img"
android:src="@mipmap/back_w" />
//设置下一首
<ImageView
android:id="@+id/listen_next_img"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="30dp"
android:layout_toRightOf="@id/listen_pause1_img"
android:src="@mipmap/next_w" />
RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/listen_play"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp">
//设置“当前歌曲时间”
<TextView
android:id="@+id/listen_current_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00" />
//设置“进度条”
<SeekBar
android:id="@+id/listen_jindutiao_sb"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
//设置“歌曲总时长”
<TextView
android:id="@+id/listen_length_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="02:30" />
LinearLayout>
RelativeLayout>
RelativeLayout>
首先对需要用到的控件进行定义:
private ImageView backIv;
private ImageView nextIv;//"下一首"
private ImageView discsmap;//"唱片"
private ImageView pauseIv;//“暂停”
private MediaPlayer mediaPlayer;//"MediaPlayer"
private ObjectAnimator animator;//运用ObjectAnimator实现转动
private TextView currentTv;//"当前时间"
private TextView totalTv;//“歌曲总时间”
private int totalTime;//“歌曲总时长,用于获取歌曲时长”
private SeekBar jindutiaoSb; //"进度条"
private boolean isStop;//“停止”
private int position;
新建方法link()进行绑定ID:
private void Link() {
backIv = findViewById(R.id.listen_back_img);
nextIv = findViewById(R.id.listen_next_img);
discsmap = findViewById(R.id.listen_changpian_img);
pauseIv = findViewById(R.id.listen_pause1_img);
currentTv = findViewById(R.id.listen_current_tv);
totalTv = findViewById(R.id.listen_length_tv);
jindutiaoSb = findViewById(R.id.listen_jindutiao_sb);
}
新建play()方法用于歌曲播放、唱片打碟功能,及进度条简要设置
private void play() {
mediaPlayer.reset();//进行重置
Bitmap disces = BitmapFactory.decodeResource(getResources(), R.mipmap.circumstance);
}
/////////////////////歌曲播放////////////////////////////////////////////////////
try {
mediaPlayer.setDataSource(music.path);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
backIv.setOnClickListener(this);
nextIv.setOnClickListener(this);
pauseIv.setOnClickListener(this);
///////////////////////////////////唱片打碟/////////////////////////////////////////////
animator = ObjectAnimator.ofFloat(discsmap, "rotation", 0f, 360.0f);
animator.setDuration(10000);
animator.setInterpolator(new LinearInterpolator());//匀速
animator.setRepeatCount(-1);//设置动画重复次数(-1代表一直转)
animator.setRepeatMode(ValueAnimator.RESTART);//动画重复模式
animator.start();
////////////////////////////////进度条/////////////////////////////////////////////////
totalTv.setText(formatTime(music.length));//获取歌曲时长并作为文本显示
totalTime = music.length;//获取歌曲时长
new Thread(new SeekBarThread()).start();//线程开始
jindutiaoSb.setMax(music.length);//设置音乐长度最大值,从而使音乐长度简单且可控
////////////////////////////指针拨动////////////////////////////////////////////////////
animator1 = ObjectAnimator.ofFloat(zhizhenmap, "rotation", -60f, 0.0f);
animator1.setDuration(900);
animator1.setRepeatCount(0);//设置动画重复次数(-1代表一直转)
animator1.start();
}
获取歌曲时长并将其转化为“分:秒”形式:
///////////////规定需要的时间形式///////////////////////
private String formatTime(int length) {
Date date = new Date(length);//调用Date方法获值
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");//规定需要形式
String TotalTime = simpleDateFormat.format(date);//转化为需要形式
return TotalTime;
}
设置事件监听:
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.listen_back_img://当点击“上一首”按钮
position--;//数组递减
if (position == -1) {
position = Common.musicList.size() - 1;
}
play();
break;
case R.id.listen_next_img:
position++;
if (position == Common.musicList.size()) {
position = 0;
}
play();
case R.id.listen_pause1_img:
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();//当正在播放时,点击“暂停”
animator.pause();
pauseIv.setImageResource(R.mipmap.start);
} else {
mediaPlayer.start();//否则,播放
pauseIv.setImageResource(R.mipmap.icon_music_circle_pause);
animator.resume();
animator1.resume();
}
default:
break;
}
}
建立销毁方法:
@Override
protected void onDestroy() {
super.onDestroy();
mediaPlayer.reset();
isStop = true;
}
运用多线程进行进度条及位置更新:
class SeekBarThread implements Runnable {
@Override
public void run() {
while (mediaPlayer != null && isStop == false) {
// 将SeekBar位置设置到当前播放位置
handler.sendEmptyMessage(mediaPlayer.getCurrentPosition());
try {
// 每100毫秒更新一次位置
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
定义Handler进行接收:
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
jindutiaoSb.setProgress(msg.what);
currentTv.setText(formatTime(msg.what));
}
};
package com.example.listviewpagerviewdemo;
public class MusicActivity extends AppCompatActivity implements View.OnClickListener {
//定义各类控件
private ImageView backIv;
private ImageView nextIv;
private int position;
private ImageView discsmap;
private ImageView pauseIv;
private MediaPlayer mediaPlayer;
private ObjectAnimator animator;
private TextView currentTv;
private TextView totalTv;
private int totalTime;
private SeekBar jindutiaoSb;
private boolean isStop;
//接受多线程信息,安卓中不允许主线程实现UI更新
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
jindutiaoSb.setProgress(msg.what);
currentTv.setText(formatTime(msg.what));
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listen_layout);
//获取传值
Intent intent = getIntent();
position = intent.getIntExtra("i", 0);//将传入的值赋给position
//获取mediaplayer
mediaPlayer = new MediaPlayer();
Link();//绑定ID方法
play();//歌曲播放及一系列操作方法
/////////////////////////获取进度条点击位置并使歌曲跳转到该位置////////////////////////////////////
jindutiaoSb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (b) {
mediaPlayer.seekTo(i);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
private void play() {
mediaPlayer.reset();
Music music = Common.musicList.get(position);//获取传来的值
Bitmap disces = BitmapFactory.decodeResource(getResources(), R.mipmap.circumstance);
/////////////////////歌曲播放////////////////////////////////////////////////////
try {
mediaPlayer.setDataSource(music.path);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
backIv.setOnClickListener(this);
nextIv.setOnClickListener(this);
pauseIv.setOnClickListener(this);
///////////////////////////////////唱片打碟/////////////////////////////////////////////
animator = ObjectAnimator.ofFloat(discsmap, "rotation", 0f, 360.0f);
animator.setDuration(10000);
animator.setInterpolator(new LinearInterpolator());//匀速
animator.setRepeatCount(-1);//设置动画重复次数(-1代表一直转)
animator.setRepeatMode(ValueAnimator.RESTART);//动画重复模式
animator.start();
////////////////////////////////进度条/////////////////////////////////////////////////
totalTv.setText(formatTime(music.length));
totalTime = music.length;
new Thread(new SeekBarThread()).start();
jindutiaoSb.setMax(music.length);
////////////////////////////指针拨动////////////////////////////////////////////////////
animator1 = ObjectAnimator.ofFloat(zhizhenmap, "rotation", -60f, 0.0f);
animator1.setDuration(900);
animator1.setRepeatCount(0);//设置动画重复次数(-1代表一直转)
animator1.start();
}
///////////////获取歌曲时常///////////////////////
private String formatTime(int length) {
Date date = new Date(length);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
String TotalTime = simpleDateFormat.format(date);
return TotalTime;
}
private void Link() {
backIv = findViewById(R.id.listen_back_img);
nextIv = findViewById(R.id.listen_next_img);
discsmap = findViewById(R.id.listen_changpian_img);
pauseIv = findViewById(R.id.listen_pause1_img);
currentTv = findViewById(R.id.listen_current_tv);
totalTv = findViewById(R.id.listen_length_tv);
jindutiaoSb = findViewById(R.id.listen_jindutiao_sb);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.listen_back_img:
position--;
if (position == -1) {
position = Common.musicList.size() - 1;
}
play();
break;
case R.id.listen_next_img:
position++;
if (position == Common.musicList.size()) {
position = 0;
}
play();
case R.id.listen_pause1_img:
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
animator.pause();
pauseIv.setImageResource(R.mipmap.start);
} else {
mediaPlayer.start();
pauseIv.setImageResource(R.mipmap.icon_music_circle_pause);
animator.resume();
animator1.resume();
}
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mediaPlayer.reset();
isStop = true;
}
class SeekBarThread implements Runnable {
@Override
public void run() {
while (mediaPlayer != null && isStop == false) {
// 将SeekBar位置设置到当前播放位置
handler.sendEmptyMessage(mediaPlayer.getCurrentPosition());
try {
// 每100毫秒更新一次位置
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
本文未将传入值进行代码讲解,仅对实现播放音乐、进度条滑动及唱片功能的实现进行了解析,需要注意的是,在除本文代码外我也单独地定义了一个工具类取名“Common”,用于存放Music类中定义的方法,在此也把代码给各位:
Common:
public class Common {
public static List musicList = new ArrayList<>();
}
Music:
public class Music {
public int length;//歌曲长度
public String path;//歌曲获取路径
}
除去获取歌曲的时长用setMax方法外,还有更偏重“小学数学好的人”的方法,即不采用获取setMax方法,而是将歌曲长度从毫秒用类型转换及计算转化为0-100内的整数:
float c = msg.what;
jindutiaoSb.setProgress((int)(c/totalTime*100));