• <td id="ae6ms"><li id="ae6ms"></li></td>
  • <xmp id="ae6ms"><td id="ae6ms"></td><table id="ae6ms"></table>
  • <table id="ae6ms"></table>
  • <td id="ae6ms"></td>
    <td id="ae6ms"></td>
  • <table id="ae6ms"></table><table id="ae6ms"><td id="ae6ms"></td></table>
  • <td id="ae6ms"></td>
  • <table id="ae6ms"><li id="ae6ms"></li></table>
  • <table id="ae6ms"></table>
    西西軟件園多重安全檢測下載網站、值得信賴的軟件下載站!
    軟件
    軟件
    文章
    搜索

    首頁編程開發Android → android手機音樂播放器實現歌詞同步

    android手機音樂播放器實現歌詞同步

    相關軟件相關文章發表評論 來源:西西整理時間:2013/5/6 20:57:51字體大?。?em class="fontsize">A-A+

    作者:西西點擊:1926次評論:0次標簽: 音樂播放器

    360聽 手機音樂播放器1.2 S60官方安裝版
    • 類型:塞班平臺應用大?。?i>1.2M語言:中文 評分:4.0
    • 標簽:
    立即下載

    最近在做一款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表示毫秒數,當然,我們最后是要將它們轉化為毫秒數處理才比較方便。

    處理完歌詞文件并得到我們想要的數據后,我們就要考慮如何在手機上滾動顯示我們的歌詞并且與我們得到的時間同步了。

    先是布局文件:


    <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" />


    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,這就是實現歌詞滾動實現的關鍵。歌詞滾動的實現思路并不復雜:將上一句歌詞向上移動,當前歌詞字體變大,顏色變黃突出顯示。

    我們需要設置位移量DY = 50。顏色和字體大小我們可以通過設置Paint來實現。

    我們注意到,在我實現的效果中,距離當前歌詞越遠的歌詞,就會變透明,這個可以通過p.setColor(Color.argb(255 - alphaValue, 245, 245, 245))來實現。

    接著就是主代碼:

    public class MainActivity extends Activity {
    private WordView mWordView;
    private List 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();
    }
    }

    歌詞的顯示需要重新開啟一個線程,因為主線程是播放歌曲的。

    代碼很簡單,功能也很簡單,最主要的是多多嘗試,多多修改,就能明白代碼的原理了。

      相關評論

      閱讀本文后您有什么感想? 已有人給出評價!

      • 8 喜歡喜歡
      • 3 頂
      • 1 難過難過
      • 5 囧
      • 3 圍觀圍觀
      • 2 無聊無聊

      熱門評論

      最新評論

      發表評論 查看所有評論(0)

      昵稱:
      表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
      字數: 0/500 (您的評論需要經過審核才能顯示)
      女人让男人桶30分钟免费视频,女人张开腿让男人桶个爽,一进一出又大又粗爽视频
    • <td id="ae6ms"><li id="ae6ms"></li></td>
    • <xmp id="ae6ms"><td id="ae6ms"></td><table id="ae6ms"></table>
    • <table id="ae6ms"></table>
    • <td id="ae6ms"></td>
      <td id="ae6ms"></td>
    • <table id="ae6ms"></table><table id="ae6ms"><td id="ae6ms"></td></table>
    • <td id="ae6ms"></td>
    • <table id="ae6ms"><li id="ae6ms"></li></table>
    • <table id="ae6ms"></table>