編輯:Android開發實例
最近在做一款android手機上的音樂播放器,學習到了很多東西,像是Fragment,ActionBar的使用等等,這裡就先介紹一下歌詞同步的實現問題。
歌詞同步的實現思路很簡單:獲取歌詞文件LRC中的時間和歌詞內容,然後在指定的時間內播放相應的內容。獲取不難,難就在於如何在手機屏幕上實現歌詞的滾動。
先上效果圖:
先從最基本的讀取歌詞文件開始:
- public class LrcHandle {
- private List<String> mWords = new ArrayList<String>();
- private List<Integer> mTimeList = new ArrayList<Integer>();
- //處理歌詞文件
- 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<String> getWords() {
- return mWords;
- }
- public List<Integer> 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表示毫秒數,當然,我們最後是要將它們轉化為毫秒數處理才比較方便。 處理完歌詞文件並得到我們想要的數據後,我們就要考慮如何在手機上滾動顯示我們的歌詞並且與我們得到的時間同步了。 先是布局文件:
- <?xml version="1.0" encoding="utf-8"?>
- <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"
- tools:context=".MainActivity" >
- <Button
- android:id="@+id/button"
- android:layout_width="60dip"
- android:layout_height="60dip"
- android:text="@string/停止" />
- <com.example.slidechange.WordView
- android:id="@+id/text"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@id/button" />
- </RelativeLayout>
WordView是自定義的TextView,它繼承自TextView:
- public class WordView extends TextView {
- private List<String> mWordsList = new ArrayList<String>();
- 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,這就是實現歌詞滾動實現的關鍵。歌詞滾動的實現思路並不復雜:將上一句歌詞向上移動,當前歌詞字體變大,顏色變黃突出顯示。
我們需要設置位移量DY = 50。顏色和字體大小我們可以通過設置Paint來實現。
我們注意到,在我實現的效果中,距離當前歌詞越遠的歌詞,就會變透明,這個可以通過p.setColor(Color.argb(255 - alphaValue, 245, 245, 245))來實現。
接著就是主代碼:
- public class MainActivity extends Activity {
- private WordView mWordView;
- private List<Integer> mTimeList;
- private MediaPlayer mPlayer;
- @SuppressLint("SdCardPath")
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button button = (Button) findViewById(R.id.button);
- button.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mPlayer.stop();
- finish();
- }
- });
- mWordView = (WordView) findViewById(R.id.text);
- mPlayer = new MediaPlayer();
- mPlayer.reset();
- LrcHandle lrcHandler = new LrcHandle();
- try {
- lrcHandler.readLRC("/sdcard/陪我去流浪.lrc");
- mTimeList = lrcHandler.getTime();
- mPlayer.setDataSource("/sdcard/陪我去流浪.mp3");
- mPlayer.prepare();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- }
- final Handler handler = new Handler();
- mPlayer.start();
- new Thread(new Runnable() {
- int i = 0;
- @Override
- public void run() {
- while (mPlayer.isPlaying()) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- mWordView.invalidate();
- }
- });
- try {
- Thread.sleep(mTimeList.get(i + 1) - mTimeList.get(i));
- } catch (InterruptedException e) {
- }
- i++;
- if (i == mTimeList.size() - 1) {
- mPlayer.stop();
- break;
- }
- }
- }
- }).start();
- }
- }
歌詞的顯示需要重新開啟一個線程,因為主線程是播放歌曲的。
代碼很簡單,功能也很簡單,最主要的是多多嘗試,多多修改,就能明白代碼的原理了。
因為本人是菜鳥,講得並不好,更多是貼出源碼好讓大家可以方便運行查看效果。
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
為要獲取網絡上的Json所以需要服務器端提供的支持。 一、創建服務器端: 服務器端項目結構: 服務器端運行效果圖: 第一步:創建業務所需的JavaBean 代
目前Android已經在只能手機市場已經具有強大的霸主地位,也吸引了越來越多的追捧者。Android的學習也越來越火。但是,報名費用確實大多人望而卻步 一、新建項
hello,大家好,本文主要介紹如何開始開發一個美觀、有情調、人見人愛的Android應用程序,已知我們在市面上有不少布局極其精美,在視覺上讓人愛不釋手的應用程序