您现在的位置: 万盛学电脑网 >> 程序编程 >> 网络编程 >> 安卓开发 >> 正文

android手机音乐播放器实现歌词同步

作者:佚名    责任编辑:admin    更新时间:2022-06-22

   最近在做一款android手机上的音乐播放器,学习到了很多东西,像是Fragment,ActionBar的使用等等,这里就先介绍一下歌词同步的实现问题。

  歌词同步的实现思路很简单:获取歌词文件LRC中的时间和歌词内容,然后在指定的时间内播放相应的内容。获取不难,难就在于如何在手机屏幕上实现歌词的滚动。

  先上效果图:

 

  先从最基本的读取歌词文件开始:

  Public class LrcHandle {

  private List mWords = new ArrayList();

  private List mTimeList = new ArrayList();

  //处理歌词文件

  public void readLRC(String path) {

  File file = new File(path);

  try {

  FileInputStream fileInputStream = new FileInputStream(file);

  InputStreamReader inputStreamReader = new InputStreamReader(

  fileInputStream, "utf-8");

  BufferedReader bufferedReader = new BufferedReader(

  inputStreamReader);

  String s = "";

  while ((s = bufferedReader.readLine()) != null) {

  addTimeToList(s);

  if ((s.indexOf("[ar:") != -1) || (s.indexOf("[ti:") != -1)

  || (s.indexOf("[by:") != -1)) {

  s = s.substring(s.indexOf(":") + 1, s.indexOf("]"));

  } else {

  String ss = s.substring(s.indexOf("["), s.indexOf("]") + 1);

  s = s.replace(ss, "");

  }

  mWords.add(s);

  }

  bufferedReader.close();

  inputStreamReader.close();

  fileInputStream.close();

  } catch (FileNotFoundException e) {

  e.printStackTrace();

  mWords.add("没有歌词文件,赶紧去下载");

  } catch (IOException e) {

  e.printStackTrace();

  mWords.add("没有读取到歌词");

  }

  }

  public List getWords() {

  return mWords;

  }

  public List getTime() {

  return mTimeList;

  }

  // 分离出时间

  private int timeHandler(String string) {

  string = string.replace(".", ":");

  String timeData[] = string.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;

  }

  private void addTimeToList(String string) {

  Matcher matcher = Pattern.compile(

  "[d{1,2}:d{1,2}([.:]d{1,2})?]").matcher(string);

  if (matcher.find()) {

  String str = matcher.group();

  mTimeList.add(new LrcHandle().timeHandler(str.substring(1,

  str.length() - 1)));

  }

  }

  }

  一般歌词文件的格式大概如下:

  [ar:艺人名]

  [ti:曲名]

  [al:专辑名]

  [by:编者(指编辑LRC歌词的人)]

  [offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。

  但也不一定,有时候并没有前面那些ar:等标识符,所以我们这里也提供了另一种解析方式。

  歌词文件中的时间格式则比较统一:[00:00.50]等等,00:表示分钟,00.表示秒数,.50表示毫秒数,当然,我们最后是要将它们转化为毫秒数处理才比较方便。

  处理完歌词文件并得到我们想要的数据后,我们就要考虑如何在手机上滚动显示我们的歌词并且与我们得到的时间同步了。

  先是布局文件:

  xmlns:tools="http://schemas.android.com/tools"

  android:layout_width="match_parent"

  android:layout_height="match_parent"

  tools:context=".MainActivity" >

  android:id="@+id/button"

  android:layout_width="60dip"

  android:layout_height="60dip"

  android:text="@string/停止" />

  android:id="@+id/text"

  android:layout_width="match_parent"

  android:layout_height="match_parent"

  android:layout_below="@id/button" />

  WordView是自定义的TextView,它继承自TextView:

  public class WordView extends TextView {

  private List mWordsList = new ArrayList();

  private Paint mLoseFocusPaint;

  private Paint mOnFocusePaint;

  private float mX = 0;

  private float mMiddleY = 0;

  private float mY = 0;

  private static final int DY = 50;

  private int mIndex = 0;

  public WordView(Context context) throws IOException {

  super(context);

  init();

  }

  public WordView(Context context, AttributeSet attrs) throws IOException {

  super(context, attrs);

  init();

  }

  public WordView(Context context, AttributeSet attrs, int defStyle)

  throws IOException {

  super(context, attrs, defStyle);

  init();

  }

  @Override

  protected void onDraw(Canvas canvas) {

  super.onDraw(canvas);

  canvas.drawColor(Color.BLACK);

  Paint p = mLoseFocusPaint;

  p.setTextAlign(Paint.Align.CENTER);

  Paint p2 = mOnFocusePaint;

  p2.setTextAlign(Paint.Align.CENTER);

  canvas.drawText(mWordsList.get(mIndex), mX, mMiddleY, p2);

  int alphaValue = 25;

  float tempY = mMiddleY;

  for (int i = mIndex - 1; i >= 0; i--) {

  tempY -= DY;

  if (tempY < 0) {

  break;

  }

  p.setColor(Color.argb(255 - alphaValue, 245, 245, 245));

  canvas.drawText(mWordsList.get(i), mX, tempY, p);

  alphaValue += 25;

  }

  alphaValue = 25;

  tempY = mMiddleY;

  for (int i = mIndex + 1, len = mWordsList.size(); i < len; i++) {

  tempY += DY;

  if (tempY > mY) {

  break;

  }

  p.setColor(Color.argb(255 - alphaValue, 245, 245, 245));

  canvas.drawText(mWordsList.get(i), mX, tempY, p);

  alphaValue += 25;

  }

  mIndex++;

  }

  @Override

  protected void onSizeChanged(int w, int h, int ow, int oh) {

  super.onSizeChanged(w, h, ow, oh);

  mX = w * 0.5f;

  mY = h;

  mMiddleY = h * 0.3f;

  }

  @SuppressLint("SdCardPath")

  private void init() throws IOException {

  setFocusable(true);

  LrcHandle lrcHandler = new LrcHandle();

  lrcHandler.readLRC("/sdcard/陪我去流浪.lrc");

  mWordsList = lrcHandler.getWords();

  mLoseFocusPaint = new Paint();

  mLoseFocusPaint.setAntiAlias(true);

  mLoseFocusPaint.setTextSize(22);

  mLoseFocusPaint.setColor(Color.WHITE);

  mLoseFocusPaint.setTypeface(Typeface.SERIF);

  mOnFocusePaint = new Paint();

  mOnFocusePaint.setAntiAlias(true);

  mOnFocusePaint.setColor(Color.YELLOW);

  mOnFocusePaint.setTextSize(30);

  mOnFocusePaint.setTypeface(Typeface.SANS_SERIF);

  }

  }

  最主要的是覆盖TextView的onDraw()和onSizeChanged()。

  在onDraw()中我们重新绘制TextView,这就是实现歌词滚动实现的关键。歌词滚动的实现思路并不复杂:将上一句歌词向上移动,当前歌词字体变大,颜色变黄突出显示。