Android应用开发--MP3音乐播放器滚动歌词实现
2013年6月2日 简、美音乐播放器开发记录
-----前话
有网友给我博客评论说,让我借鉴好的Android代码,代码贴出来的时候最好整体先看一下。其实小巫也有参考过别人的代码,主要是具体看某一个功能是怎么实现的,但是因为开发的思路不一样,只能说自己去写一些符合自己思路的代码。编写代码过程中,或多或少有纰漏之处,但基本上能实现功能就行了。小巫的功底还不够,不具备很强的重构代码的能力,一直都是以最直观的想法来编程,并没有太过关注性能的优化啥的,因为我也没发现自己开发的这款音乐播放器用起来不爽。不过,小巫会一直学习的,努力提升自己的编程水平,争取生产出优美的代码供朋友们参考。
-----主题
这篇博客的主题是:“滚动歌词的实现”
要的效果如下:
----实现过程
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);}/** * 绘画歌词 */@Overrideprotected 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大小改变的时候调用的方法 */@Overrideprotected 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 * @authorwwj *处理歌词的类 */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() {@Overridepublic 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;}
其实,小巫还想实现可以拖动歌词来控制播放进度,还有自动搜索歌词等更加完备的实现。