其实到后面就需要我们如何显示歌词,对于歌词的同步显示还是比较好实现的,主要通过判断当前播放的时间和每个结点的歌词的时间的大小,来同步对应到结点的数据,现在就是如何来实现这个问题。
其实,这个时候就需要自定义控件来实现。
第一步需要自定义View的属性。
第二步需要实现在View的构造方法中获得我们自定义的属性。
主要通过初始化函数,init()
第三步,重写ondraw函数。
LrcView.java
package com.flashmusic.View;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;
import com.flashmusic.tool.LrcInfo;
import com.flashmusic.tool.LrcList;
import java.util.Map;
/**
* Created by zhouchenglin on 2016/4/20.
*/
public class LrcView extends TextView {
private float width; //歌词视图宽度
private float height; //歌词视图高度
private Paint currentPaint; //当前画笔对象
private Paint notCurrentPaint; //非当前画笔对象
private float textHeight = 65; //文本高度
private float textMaxSize = 50;
private float textSize = 40; //文本大小
private int index = 0; //list集合下标
private LrcInfo infos; //歌词信息
public void setmLrcList(LrcInfo infos) {
this.infos = infos;
}
public LrcView(Context context) {
super(context);
init();
}
public LrcView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public LrcView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setFocusable(true); //设置可对焦
//显示歌词部分
currentPaint = new Paint();
currentPaint.setAntiAlias(true); //设置抗锯齿,让文字美观饱满
currentPaint.setTextAlign(Paint.Align.CENTER);//设置文本对齐方式
//非高亮部分
notCurrentPaint = new Paint();
notCurrentPaint.setAntiAlias(true);
notCurrentPaint.setTextAlign(Paint.Align.CENTER);
}
/**
* 绘画歌词
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (canvas == null) {
return;
}
currentPaint.setColor(Color.argb(210, 251, 248, 29));
notCurrentPaint.setColor(Color.argb(140, 255, 255, 255));
currentPaint.setTextSize(textMaxSize);
currentPaint.setTypeface(Typeface.SERIF);
notCurrentPaint.setTextSize(textSize);
notCurrentPaint.setTypeface(Typeface.DEFAULT);
try {
setText("");
canvas.drawText(infos.getLrcLists().get(index).getContent(), width / 2, height / 2, currentPaint);
float tempY = height / 2;
//画出本句之前的句子
for (int i = index - 1; i >= 0; i--) {
//向上推移
tempY = tempY - textHeight;
canvas.drawText(infos.getLrcLists().get(i).getContent(), width / 2, tempY, notCurrentPaint);
}
tempY = height / 2;
//画出本句之后的句子
for (int i = index + 1; i < infos.getLrcLists().size(); i++) {
//往下推移
tempY = tempY + textHeight;
canvas.drawText(infos.getLrcLists().get(i).getContent(), width / 2, tempY, notCurrentPaint);
}
} catch (Exception e) {
setText("...木有歌词文件,赶紧去下载...");
}
}
/**
* 当view大小改变的时候调用的方法
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.width = w;
this.height = h;
}
public void setIndex(int index) {
this.index = index;
}
}
这个类是别人实现了,我仔细研读了下,觉得写的不错,通过下标的判断来实现文本的显示,这个比较不错,值得学习。
接下来就需要在Musicservice.java里面初始化函数的获得。
public void initLrc() {
//建立歌词对象
LrcParse lrcParser = new LrcParse(path);
//读歌词,并将数据传给歌词信息类
lrcInfo = lrcParser.readLrc();
//获得歌词中的结点
lrcLists = lrcInfo.getLrcLists();
//在musicActivity里面设置静态来共享数据
MusicActivity.lrcView.setmLrcList(lrcInfo);
//切换带动画显示歌词
MusicActivity.lrcView.setAnimation(AnimationUtils.loadAnimation(MusicService.this, R.anim.alpha_z));
mHandler.post(mRunnable);
}
Runnable mRunnable = new Runnable() {
@Override
public void run() {
MusicActivity.lrcView.setIndex(lrcIndex());
MusicActivity.lrcView.invalidate();
mHandler.postDelayed(mRunnable, 100);
}
};
Handle主要通过Runnable来实现MusicActivity里面歌词的同步,但同步还是需要判断自定义控件里面的index属性。
lrcIndex函数的判断,通过比较时间来获得具体的index.
public int lrcIndex() {
if (mediaPlayer.isPlaying()) {
currentTime = mediaPlayer.getCurrentPosition();
duration = mediaPlayer.getDuration();
}
if (currentTime < duration) {
for (int i = 0; i < lrcLists.size(); i++) {
if (i < lrcLists.size() - 1) {
if (currentTime < lrcLists.get(i).getCurrentTime() && i == 0) {
index = i;
}
if ((currentTime > lrcLists.get(i).getCurrentTime())&& currentTime < lrcLists.get(i+1).getCurrentTime()) {
index = i;
}
}
if ((i == lrcLists.size() - 1)&& currentTime > lrcLists.get(i).getCurrentTime()) {
index = i;
}
}
}
return index;
}
接下来就是如何调用初始化函数,因为我们每次播放的时候,才显示歌词的同步,所以我将他放在初始化的函数里面,问题就可以搞定了。
看看play函数,只需要添加一行代码就完成了。
private void play(int currentTime) {
try {
mediaPlayer.reset();// 把各项参数恢复到初始状态
mediaPlayer.setDataSource(path);
mediaPlayer.prepare(); // 进行缓冲
mediaPlayer.setOnPreparedListener(new PreparedListener(currentTime));// 注册一个监听器
initLrc();
//更新播放状态
Intent intent = new Intent(PLAY_STATUE);
// 发送播放完毕的信号,更新播放状态
intent.putExtra("playstatue", true);
sendBroadcast(intent);
mHandler.sendEmptyMessage(1);
} catch (Exception e) {
e.printStackTrace();
}
}
看看实现的效果:
对于文章中出现的有些类名,可以看之前写的系列文章。
现在一款本地音乐播放器的功能基本实现了,这里面用到了很多的知识,对android这个系统的了解又更加加深了理解,感谢前辈们的博客,让我受益匪浅,谢谢技术的分享,现在我也是为了更多的人,来分享自己的博客。让我们在技术的海洋中不断进步。