2013 年 6 月 2 日 简、美音乐播放器开发记录 |
-----主题 |
这篇博客的主题是:“滚动歌词的实现” |
要的效果如下: |
|
----实现过程 |
1 . 建立歌词内容实体类 |
2 . 自定义View |
3 . 加入布局文件 |
4 . 编写歌词处理类 |
5 . 在Service里面实现同步更新歌词 |
----代码实现 |
--LrcContent.java |
package com.wwj.sb.domain; |
/** |
* 2013/6/1 |
* @author wwj |
* 歌词实体类 |
*/ |
public class LrcContent { |
private String lrcStr; //歌词内容 |
private int lrcTime; //歌词当前时间 |
public String getLrcStr() { |
return lrcStr; |
} |
public void setLrcStr(String lrcStr) { |
this .lrcStr = lrcStr; |
} |
public int getLrcTime() { |
return lrcTime; |
} |
public void setLrcTime( int lrcTime) { |
this .lrcTime = lrcTime; |
} |
} |
--LrcView.java |
package com.wwj.sb.custom; |
import java.util.ArrayList; |
import java.util.List; |
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 com.wwj.sb.domain.LrcContent; |
/** |
* 自定义绘画歌词,产生滚动效果 |
* @author wwj |
* |
*/ |
public class LrcView extends android.widget.TextView { |
private float width; //歌词视图宽度 |
private float height; //歌词视图高度 |
private Paint currentPaint; //当前画笔对象 |
private Paint notCurrentPaint; //非当前画笔对象 |
private float textHeight = 25 ; //文本高度 |
private float textSize = 18 ; //文本大小 |
private int index = 0 ; //list集合下标 |
|
|
private List<LrcContent> mLrcList = new ArrayList<LrcContent>(); |
|
public void setmLrcList(List<LrcContent> mLrcList) { |
this .mLrcList = mLrcList; |
} |
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( 24 ); |
currentPaint.setTypeface(Typeface.SERIF); |
|
notCurrentPaint.setTextSize(textSize); |
notCurrentPaint.setTypeface(Typeface.DEFAULT); |
|
try { |
setText( "" ); |
canvas.drawText(mLrcList.get(index).getLrcStr(), width / 2 , height / 2 , currentPaint); |
|
float tempY = height / 2 ; |
//画出本句之前的句子 |
for ( int i = index - 1 ; i >= 0 ; i--) { |
//向上推移 |
tempY = tempY - textHeight; |
canvas.drawText(mLrcList.get(i).getLrcStr(), width / 2 , tempY, notCurrentPaint); |
} |
tempY = height / 2 ; |
//画出本句之后的句子 |
for ( int i = index + 1 ; i < mLrcList.size(); i++) { |
//往下推移 |
tempY = tempY + textHeight; |
canvas.drawText(mLrcList.get(i).getLrcStr(), width / 2 , tempY, notCurrentPaint); |
} |
} catch (Exception e) { |
setText( "...木有歌词文件,赶紧去下载..." ); |
} |
} |
/** |
* 当view大小改变的时候调用的方法 |
*/ |
@Override |
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; |
} |
|
} |
--LrcProcess.java |
package com.wwj.sb.custom; |
import java.io.BufferedReader; |
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileNotFoundException; |
import java.io.IOException; |
import java.io.InputStreamReader; |
import java.util.ArrayList; |
import java.util.List; |
import android.util.Xml.Encoding; |
import android.widget.SlidingDrawer; |
import com.wwj.sb.domain.LrcContent; |
/** |
* 2013/6/1 |
* @author wwj |
* 处理歌词的类 |
*/ |
public class LrcProcess { |
private List<LrcContent> lrcList; //List集合存放歌词内容对象 |
private LrcContent mLrcContent; //声明一个歌词内容对象 |
/** |
* 无参构造函数用来实例化对象 |
*/ |
public LrcProcess() { |
mLrcContent = new LrcContent(); |
lrcList = new ArrayList<LrcContent>(); |
} |
|
/** |
* 读取歌词 |
* @param path |
* @return |
*/ |
public String readLRC(String path) { |
//定义一个StringBuilder对象,用来存放歌词内容 |
StringBuilder stringBuilder = new StringBuilder(); |
File f = new File(path.replace( ".mp3" , ".lrc" )); |
|
try { |
//创建一个文件输入流对象 |
FileInputStream fis = new FileInputStream(f); |
InputStreamReader isr = new InputStreamReader(fis, "utf-8" ); |
BufferedReader br = new BufferedReader(isr); |
String s = "" ; |
while ((s = br.readLine()) != null ) { |
//替换字符 |
s = s.replace( "[" , "" ); |
s = s.replace( "]" , "@" ); |
|
//分离“@”字符 |
String splitLrcData[] = s.split( "@" ); |
if (splitLrcData.length > 1 ) { |
mLrcContent.setLrcStr(splitLrcData[ 1 ]); |
|
//处理歌词取得歌曲的时间 |
int lrcTime = time2Str(splitLrcData[ 0 ]); |
|
mLrcContent.setLrcTime(lrcTime); |
|
//添加进列表数组 |
lrcList.add(mLrcContent); |
|
//新创建歌词内容对象 |
mLrcContent = new LrcContent(); |
} |
} |
} catch (FileNotFoundException e) { |
e.printStackTrace(); |
stringBuilder.append( "木有歌词文件,赶紧去下载!..." ); |
} catch (IOException e) { |
e.printStackTrace(); |
stringBuilder.append( "木有读取到歌词哦!" ); |
} |
return stringBuilder.toString(); |
} |
/** |
* 解析歌词时间 |
* 歌词内容格式如下: |
* [00:02.32]陈奕迅 |
* [00:03.43]好久不见 |
* [00:05.22]歌词制作 王涛 |
* @param timeStr |
* @return |
*/ |
public int time2Str(String timeStr) { |
timeStr = timeStr.replace( ":" , "." ); |
timeStr = timeStr.replace( "." , "@" ); |
|
String timeData[] = timeStr.split( "@" ); //将时间分隔成字符串数组 |
|
//分离出分、秒并转换为整型 |
int minute = Integer.parseInt(timeData[ 0 ]); |
int second = Integer.parseInt(timeData[ 1 ]); |
int millisecond = Integer.parseInt(timeData[ 2 ]); |
|
//计算上一行与下一行的时间转换为毫秒数 |
int currentTime = (minute * 60 + second) * 1000 + millisecond * 10 ; |
return currentTime; |
} |
public List<LrcContent> getLrcList() { |
return lrcList; |
} |
} |
加入布局文件: |
<com.wwj.sb.custom.LrcView |
android:id= "@+id/lrcShowView" |
android:layout_width= "match_parent" |
android:layout_height= "200dip" |
android:layout_above= "@+id/footer_layout" |
android:layout_below= "@+id/header_layout" |
android:layout_centerHorizontal= "true" /> |
--在Service.java中的实现,这里就不贴完整的Service类了,主要是如何在Service实现歌词同步的。 |
声明变量: |
private LrcProcess mLrcProcess; //歌词处理 |
private List<LrcContent> lrcList = new ArrayList<LrcContent>(); //存放歌词列表对象 |
private int index = 0 ; //歌词检索值 |
核心实现代码: |
/** |
* 初始化歌词配置 |
*/ |
public void initLrc(){ |
mLrcProcess = new LrcProcess(); |
//读取歌词文件 |
mLrcProcess.readLRC(mp3Infos.get(current).getUrl()); |
//传回处理后的歌词文件 |
lrcList = mLrcProcess.getLrcList(); |
PlayerActivity.lrcView.setmLrcList(lrcList); |
//切换带动画显示歌词 |
PlayerActivity.lrcView.setAnimation(AnimationUtils.loadAnimation(PlayerService. this ,R.anim.alpha_z)); |
handler.post(mRunnable); |
} |
Runnable mRunnable = new Runnable() { |
|
@Override |
public void run() { |
PlayerActivity.lrcView.setIndex(lrcIndex()); |
PlayerActivity.lrcView.invalidate(); |
handler.postDelayed(mRunnable, 100 ); |
} |
}; |
/** |
* 根据时间获取歌词显示的索引值 |
* @return |
*/ |
public int lrcIndex() { |
if (mediaPlayer.isPlaying()) { |
currentTime = mediaPlayer.getCurrentPosition(); |
duration = mediaPlayer.getDuration(); |
} |
if (currentTime < duration) { |
for ( int i = 0 ; i < lrcList.size(); i++) { |
if (i < lrcList.size() - 1 ) { |
if (currentTime < lrcList.get(i).getLrcTime() && i == 0 ) { |
index = i; |
} |
if (currentTime > lrcList.get(i).getLrcTime() |
&& currentTime < lrcList.get(i + 1 ).getLrcTime()) { |
index = i; |
} |
} |
if (i == lrcList.size() - 1 |
&& currentTime > lrcList.get(i).getLrcTime()) { |
index = i; |
} |
} |
} |
return index; |
} |
其实,小巫还想实现可以拖动歌词来控制播放进度,还有自动搜索歌词等更加完备的实现。 |
by: 发表于:2017-12-21 09:50:55 顶(0) | 踩(0) 回复
??
回复评论