直接代码:
package com.example.musicplayer;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.example.utils.Lyric;
import java.util.List;
public class LyricView extends View {
private int height,width;
private int offset = 0;
private float textSize = 31;
private int LineHeight = 45;
private int currentLine = 0;
private int targetOffset = 0;
private OnChangeProgress onChangeProgress;
private MediaPlayer mediaPlayer;
private Context context;
private Paint hPaint, dPaint,pPaint;
private List<Lyric> LyricList;
private boolean isTouch = false;
private int offset_touch = 0;
onLyricChangeListener lyricChangeListener;
public void setLyricChangeListener(onLyricChangeListener lyricChangeListener) {
this.lyricChangeListener = lyricChangeListener;
}
public void setCurrentLine(int currentLine){
if (currentLine == this.currentLine)return;
this.currentLine = currentLine;
targetOffset = currentLine * LineHeight;
invalidate();
}
public void setMediaPlayer(android.media.MediaPlayer mediaPlayer) {
this.mediaPlayer = mediaPlayer;
mediaPlayer.setOnCompletionListener(listener);
}
MediaPlayer.OnCompletionListener listener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
currentLine = 0;
setCurrentLine(currentLine);
mediaPlayer.seekTo(0);
mediaPlayer.start();
Log.e("TAG","重新播放");
}
};
public void setLyricList(List<Lyric> LyricList) {
offset = 0;
currentLine = 0;
targetOffset = 0;
this.LyricList = LyricList;
invalidate();
}
public void reset(){
currentLine = 0;
targetOffset = 0;
offset = 0;
invalidate();
}
public LyricView(Context context) {
super(context);
this.context = context;
init();
}
public LyricView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
private void init(){
hPaint = new Paint();
hPaint.setTextSize(textSize*1.3f);
hPaint.setColor(context.getResources().getColor(R.color.highlighted_color));
hPaint.setAntiAlias(true);
dPaint = new Paint();
dPaint.setTextSize(textSize);
dPaint.setColor(context.getResources().getColor(R.color.default_color));
dPaint.setAntiAlias(true);
pPaint = new Paint();
pPaint.setTextSize((float) (textSize*0.8));
pPaint.setColor(context.getResources().getColor(R.color.play_color));
pPaint.setAntiAlias(true);
}
public void setTextSize(float textSize) {
this.textSize = textSize;
hPaint.setTextSize(textSize*1.3f);
dPaint.setTextSize(textSize);
LineHeight = (int) (textSize * 1.8);
}
int down_y;
int move_y;
int preOffset = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if (isTouch){
if (rectF.contains(new RectF(event.getX()-1,event.getY()-1,event.getX()+1,event.getY()+1))){
if (onChangeProgress!=null)onChangeProgress.onChanged(Math.abs(offset/LineHeight));
currentLine = Math.abs(offset/LineHeight);
mediaPlayer.seekTo(LyricList.get(currentLine).getTime());
isTouch = false;
if (currentLine<LyricList.size())
if (lyricChangeListener!=null)lyricChangeListener.onChange(LyricList.get(currentLine).getLyric());
invalidate();
}
}
handler.removeCallbacks(runnable);
isTouch = true;
down_y = (int) event.getY();
preOffset = offset;
break;
case MotionEvent.ACTION_MOVE:
move_y = (int) event.getY();
offset = preOffset - (move_y - down_y);
if (offset<=0)offset=0;
else if (offset>LineHeight*(LyricList.size()-1))offset = LineHeight*(LyricList.size()-1);
invalidate();
break;
case MotionEvent.ACTION_UP:
handler.postDelayed(runnable,2000);
break;
}
return true;
}
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
isTouch = false;
preOffset = 0;
invalidate();
}
};
RectF rectF = new RectF();
Path path = new Path();
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
targetOffset = currentLine * LineHeight;
canvas.save();
canvas.translate(0, -offset);
drawLrc(canvas);
if (offset != targetOffset && !isTouch) {
if (offset < targetOffset){
offset+=(1+Math.abs(offset-targetOffset)/LineHeight);
}else{
offset-=(1+Math.abs(offset-targetOffset)/LineHeight);
}
invalidate();
}
canvas.restore();
if (isTouch){
int line = Math.abs(offset/LineHeight);
if (line>=LyricList.size())return;
String time = String.format("%02d:%02d",LyricList.get(line).getMinutes(),LyricList.get(line).getSeconds());
pPaint.setColor(context.getResources().getColor(R.color.play_back_color));
rectF.set(width-textSize*3, height/2-textSize*1.5f,
width-textSize*0.3f, height/2-textSize*0.5f);
canvas.drawRoundRect(rectF,10,10,pPaint);
pPaint.setColor(context.getResources().getColor(R.color.play_color));
canvas.drawText(time,rectF.right-pPaint.getTextSize()*2.5f, rectF.bottom-(rectF.height())*0.3f,pPaint);
path.moveTo(rectF.left+pPaint.getTextSize()/2,rectF.bottom-(rectF.height())*0.3f-pPaint.getTextSize()*0.8f);
path.lineTo(rectF.left+pPaint.getTextSize()/2*2,rectF.bottom-(rectF.height())*0.3f-pPaint.getTextSize()*0.4f);
path.lineTo(rectF.left+pPaint.getTextSize()/2,rectF.bottom-(rectF.height())*0.3f);
canvas.drawPath(path,pPaint);
}
playing();
invalidate();
}
private void playing(){
if (mediaPlayer != null){
if (mediaPlayer.isPlaying()&¤tLine < LyricList.size()) {
if (mediaPlayer.getCurrentPosition() >= LyricList.get(currentLine).getTime()) {
if (currentLine<LyricList.size())
if (lyricChangeListener!=null)lyricChangeListener.onChange(LyricList.get(currentLine).getLyric());
currentLine++;
}
}
}
}
private void drawLrc(Canvas canvas){
if (LyricList==null)return;
int len = LyricList.size();
for (int i = 0; i < len; i++) {
String lrc = LyricList.get(i).getLyric();
if (i == currentLine-1||(Math.abs(offset/LineHeight)==i&&isTouch)) {
canvas.drawText(lrc, width / 2 - measureLineSpace(lrc,hPaint.getTextSize()) / 2,
height / 2 + i * LineHeight, hPaint);
}else{
canvas.drawText(lrc, width / 2 - measureLineSpace(lrc,dPaint.getTextSize()) / 2,
height / 2 + i * LineHeight,dPaint);
}
}
}
private int measureLineSpace(String lrc,float textSize){
int lineSpace = 0;
for (char c : lrc.toCharArray()) {
if (c >= ' ' && c <= '~'){
lineSpace += textSize/2;
}else lineSpace += textSize;
}
return lineSpace;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
width = getWidth();
height = getHeight();
textSize = width/20;
setTextSize(textSize);
}
public void setOnChangeProgress(OnChangeProgress onChangeProgress) {
this.onChangeProgress = onChangeProgress;
}
public abstract interface OnChangeProgress{
public abstract void onChanged(int line);
}
public interface onLyricChangeListener{
void onChange(String lyric);
}
}
一些歌词类Lyric查看《Android下载并解析Lrc歌词文件》