1. 程式人生 > >android程式設計例項-音樂播放器之歌詞顯示

android程式設計例項-音樂播放器之歌詞顯示

        今天分享一個歌詞顯示的專案,首先讓我們來看看一般歌詞是什麼樣的格式,就拿神曲《小蘋果》來說的,請看歌詞:

[00:00.91]小蘋果
[00:01.75]作詞:王太利 作曲:王太利
[00:02.47]演唱:筷子兄弟
[00:03.32]
[00:17.40]我種下一顆種子
[00:19.12]終於長出了果實
[00:21.04]今天是個偉大日子
[00:25.10]摘下星星送給你
[00:26.79]拽下月亮送給你
[00:28.77]讓太陽每天為你升起
[00:31.25]
[00:32.67]變成蠟燭燃燒自己
[00:34.47]只為照亮你
[00:36.44]把我一切都獻給你
[00:38.22]只要你歡喜
[00:40.19]你讓我每個明天都
[00:42.15]變得有意義
[00:44.04]生命雖短愛你永遠
[00:46.08]不離不棄
[00:47.81]
[00:48.15]你是我的小呀小蘋果兒
[00:51.92]怎麼愛你都不嫌多
[00:55.26]紅紅的小臉兒溫暖我的心窩
[00:59.33]點亮我生命的火  火火火火
[01:03.45]你是我的小呀小蘋果兒
[01:07.06]就像天邊最美的雲朵
[01:10.61]春天又來到了花開滿山坡
[01:14.70]種下希望就會收穫

新建一個java類Lyrc來表示歌詞
public class Lyrc {
	public String lrcString;    //歌詞
	public int sleepTime;     //一行歌詞的持續時間
	public int timePoint;      //一行歌詞結束的時間點
}

新建java類對歌詞進行處理,歌詞中一行裡有時間[00:48.15],用[ ]括起來,緊接著是歌詞內容,我們一行一行的將歌詞讀入,並將時間和歌詞分離。
public class LyrcUtil {
	private static List<Lyrc> lyrcList;
	/**
	 * 讀取歌詞檔案檔案
	 * @param f
	 * @return
	 */
	public static List<Lyrc> readLRC(File f) {
		try {
			if (f == null || !f.exists()) {
				lyrcList = null;
			} else {
				lyrcList = new Vector<Lyrc>();
				InputStream is = new BufferedInputStream(new FileInputStream(f));
				BufferedReader br = new BufferedReader(new InputStreamReader(is, getCharset(f)));
				String strTemp = "";
				while ((strTemp = br.readLine()) != null) {
					strTemp = analyzeLRC(strTemp);
				}
				br.close();
				is.close();
				// 對歌詞進行排序
				Collections.sort(lyrcList, new Sort());
				// 計算每行歌詞的停留時間
				for (int i = 0; i < lyrcList.size(); i++) {

					Lyrc one = lyrcList.get(i);
					if (i + 1 < lyrcList.size()) {
						Lyrc two = lyrcList.get(i + 1);
						one.sleepTime = two.timePoint - one.timePoint;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return lyrcList;
	}

	/**
	 * 處理歌詞中的一行內容
	 * 
	 * @param text
	 * @return
	 */
	private static String analyzeLRC(String text) {
		try {
			int pos1 = text.indexOf("[");
			int pos2 = text.indexOf("]");

			if (pos1 >= 0 && pos2 != -1) {
				Long time[] = new Long[getPossiblyTagCount(text)];
				time[0] = timeToLong(text.substring(pos1 + 1, pos2));
				if (time[0] == -1)
					return "";
				String strLineRemaining = text;
				int i = 1;
				while (pos1 >= 0 && pos2 != -1) {

					strLineRemaining = strLineRemaining.substring(pos2 + 1);
					pos1 = strLineRemaining.indexOf("[");
					pos2 = strLineRemaining.indexOf("]");
					if (pos2 != -1) {
						time[i] = timeToLong(strLineRemaining.substring(pos1 + 1, pos2));
						if (time[i] == -1)
							return ""; // LRCText
						i++;
					}
				}

				Lyrc tl = null;
				for (int j = 0; j < time.length; j++) {
					if (time[j] != null) {
						tl = new Lyrc();
						tl.timePoint = time[j].intValue();
						tl.lrcString = strLineRemaining;
						lyrcList.add(tl);
					}
				}
				return strLineRemaining;
			} else
				return "";
		} catch (Exception e) {
			return "";
		}
	}

	private static int getPossiblyTagCount(String Line) {
		String strCount1[] = Line.split("\\[");
		String strCount2[] = Line.split("\\]");
		if (strCount1.length == 0 && strCount2.length == 0)
			return 1;
		else if (strCount1.length > strCount2.length)
			return strCount1.length;
		else
			return strCount2.length;
	}

	/**
	 * 時間轉換
	 * 
	 * @param Time
	 * @return
	 */
	public static long timeToLong(String Time) {
		try {
			String[] s1 = Time.split(":");
			int min = Integer.parseInt(s1[0]);
			String[] s2 = s1[1].split("\\.");
			int sec = Integer.parseInt(s2[0]);
			int mill = 0;
			if (s2.length > 1)
				mill = Integer.parseInt(s2[1]);
			return min * 60 * 1000 + sec * 1000 + mill * 10;
		} catch (Exception e) {
			return -1;
		}
	}

	/**
	 * 判斷檔案的編碼型別
	 * 
	 * @param file
	 * @return
	 */
	public static String getCharset(File file) {
		String charset = "GBK";
		byte[] first3Bytes = new byte[3];
		try {
			boolean checked = false;
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
			bis.mark(0);
			int read = bis.read(first3Bytes, 0, 3);
			if (read == -1)
				return charset;
			if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
				charset = "UTF-16LE";
				checked = true;
			} else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
				charset = "UTF-16BE";
				checked = true;
			} else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF) {
				charset = "UTF-8";
				checked = true;
			}
			bis.reset();
			if (!checked) {
				int loc = 0;
				while ((read = bis.read()) != -1) {
					loc++;
					if (read >= 0xF0)
						break;
					if (0x80 <= read && read <= 0xBF)
						break;
					if (0xC0 <= read && read <= 0xDF) {
						read = bis.read();
						if (0x80 <= read && read <= 0xBF)
							continue;
						else
							break;
					} else if (0xE0 <= read && read <= 0xEF) {
						read = bis.read();
						if (0x80 <= read && read <= 0xBF) {
							read = bis.read();
							if (0x80 <= read && read <= 0xBF) {
								charset = "UTF-8";
								break;
							} else
								break;
						} else
							break;
					}
				}
			}
			bis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return charset;
	}
    //根據時間順序進行排序
	private static class Sort implements Comparator<Lyrc> {
		public Sort() {
		}

		public int compare(Lyrc tl1, Lyrc tl2) {
			return sortUp(tl1, tl2);
		}

		private int sortUp(Lyrc tl1, Lyrc tl2) {
			if (tl1.timePoint < tl2.timePoint)
				return -1;
			else if (tl1.timePoint > tl2.timePoint)
				return 1;
			else
				return 0;
		}
	}
}

新建顯示歌詞的類LrcView
<pre name="code" class="java">public class LrcView extends TextView {

	private List<Lyrc> lyrcList;
	private int current = 0;
	// 行的間距
	private int lineSpacing = 30;

	// 當前正在繪製的行
	private Paint currentPaint;
	private int currentColor = Color.GREEN;
	private int currentSize = 18;
	private Typeface currentTypeface = Typeface.DEFAULT_BOLD;

	private Paint ortherPaint;
	private int ortherColor = Color.BLACK;
	private int ortherSize = 15;
	private Typeface ortherTypeface = Typeface.SERIF;

	private Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			invalidate();  //重複onDraw()方法
		}
	};

	public LrcView(Context context, AttributeSet attrs) {
		super(context, attrs);

		lyrcList = LyrcUtil.readLRC(new File("/sdcard/小蘋果.lrc"));

		currentPaint = new Paint();

		currentPaint.setColor(currentColor);
		currentPaint.setTextSize(currentSize);
		currentPaint.setTextAlign(Align.CENTER);
		currentPaint.setTypeface(currentTypeface);

		ortherPaint = new Paint();

		ortherPaint.setColor(ortherColor);
		ortherPaint.setTextSize(ortherSize);
		ortherPaint.setTextAlign(Align.CENTER);
		ortherPaint.setTypeface(ortherTypeface);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// getWidth();
		// getHeight();
		if (current < lyrcList.size()) {
			if (lyrcList != null && lyrcList.size() > 0) {
				Lyrc lyrc =null;
				for (int i = current - 1; i >= 0; i--) {
					lyrc = lyrcList.get(i);
					canvas.drawText(lyrc.lrcString, getWidth() / 2, getHeight() / 2 + lineSpacing * (i - current), ortherPaint);
				}
				lyrc=lyrcList.get(current);
				canvas.drawText(lyrc.lrcString, getWidth() / 2, getHeight() / 2, currentPaint);

				for (int i = current + 1; i < lyrcList.size(); i++) {
					lyrc = lyrcList.get(i);
					canvas.drawText(lyrc.lrcString, getWidth() / 2, getHeight() / 2 + lineSpacing * (i - current), ortherPaint);
				}
				lyrc=lyrcList.get(current);
				handler.sendEmptyMessageDelayed(10, lyrc.sleepTime);
				current++;
			} else {
				canvas.drawText("未找到歌詞", getWidth() / 2, getHeight() / 2, currentPaint);
			}
		}
		super.onDraw(canvas);
	}

}

在res/layout下建立一個activity_main.xml,將LrcView 包含進來
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <com.zhuzhu.LrcView
        android:id="@+id/lrcview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

在Activity中進行顯示
public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

}