文章出自:http://blog.csdn.net/wwj_748/article/details/9006567
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);
- }
- /**
- * 绘画歌词
- */
- @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; //歌词检索值
核心实现代码:
- <span style="white-space:pre"> </span>/**
- * 初始化歌词配置
- */
- 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;
- }
其实,小巫还想实现可以拖动歌词来控制播放进度,还有自动搜索歌词等更加完备的实现。