日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴(lài)的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁(yè)編程開(kāi)發(fā)Android → android手機(jī)音樂(lè)播放器實(shí)現(xiàn)歌詞同步

      android手機(jī)音樂(lè)播放器實(shí)現(xiàn)歌詞同步

      相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來(lái)源:西西整理時(shí)間:2013/5/6 20:57:51字體大。A-A+

      作者:西西點(diǎn)擊:1926次評(píng)論:0次標(biāo)簽: 音樂(lè)播放器

      • 類(lèi)型:塞班平臺(tái)應(yīng)用大。1.2M語(yǔ)言:中文 評(píng)分:4.0
      • 標(biāo)簽:
      立即下載

      最近在做一款android手機(jī)上的音樂(lè)播放器,學(xué)習(xí)到了很多東西,像是Fragment,ActionBar的使用等等,這里就先介紹一下歌詞同步的實(shí)現(xiàn)問(wèn)題。

      歌詞同步的實(shí)現(xiàn)思路很簡(jiǎn)單:獲取歌詞文件LRC中的時(shí)間和歌詞內(nèi)容,然后在指定的時(shí)間內(nèi)播放相應(yīng)的內(nèi)容。獲取不難,難就在于如何在手機(jī)屏幕上實(shí)現(xiàn)歌詞的滾動(dòng)。

      先上效果圖:

      先從最基本的讀取歌詞文件開(kāi)始:

      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("沒(méi)有歌詞文件,趕緊去下載");
              } catch (IOException e) {
                  e.printStackTrace();
                  mWords.add("沒(méi)有讀取到歌詞");
              }
          }
         public List getWords() {
              return mWords;
         }
      
          public List getTime() {
              return mTimeList;
          }
      
          // 分離出時(shí)間
          private int timeHandler(String string) {
             string = string.replace(".", ":");
             String timeData[] = string.split(":");
      // 分離出分、秒并轉(zhuǎn)換為整型
              int minute = Integer.parseInt(timeData[0]);
              int second = Integer.parseInt(timeData[1]);
              int millisecond = Integer.parseInt(timeData[2]);
      
              // 計(jì)算上一行與下一行的時(shí)間轉(zhuǎn)換為毫秒數(shù)
              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:專(zhuān)輯名]

      [by:編者(指編輯LRC歌詞的人)]

      [offset:時(shí)間補(bǔ)償值] 其單位是毫秒,正值表示整體提前,負(fù)值相反。這是用于總體調(diào)整顯示快慢的。

      但也不一定,有時(shí)候并沒(méi)有前面那些ar:等標(biāo)識(shí)符,所以我們這里也提供了另一種解析方式。

      歌詞文件中的時(shí)間格式則比較統(tǒng)一:[00:00.50]等等,00:表示分鐘,00.表示秒數(shù),.50表示毫秒數(shù),當(dāng)然,我們最后是要將它們轉(zhuǎn)化為毫秒數(shù)處理才比較方便。

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

      先是布局文件:


      <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,這就是實(shí)現(xiàn)歌詞滾動(dòng)實(shí)現(xiàn)的關(guān)鍵。歌詞滾動(dòng)的實(shí)現(xiàn)思路并不復(fù)雜:將上一句歌詞向上移動(dòng),當(dāng)前歌詞字體變大,顏色變黃突出顯示。

      我們需要設(shè)置位移量DY = 50。顏色和字體大小我們可以通過(guò)設(shè)置Paint來(lái)實(shí)現(xiàn)。

      我們注意到,在我實(shí)現(xiàn)的效果中,距離當(dāng)前歌詞越遠(yuǎn)的歌詞,就會(huì)變透明,這個(gè)可以通過(guò)p.setColor(Color.argb(255 - alphaValue, 245, 245, 245))來(lái)實(shí)現(xiàn)。

      接著就是主代碼:

      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();
      }
      }

      歌詞的顯示需要重新開(kāi)啟一個(gè)線(xiàn)程,因?yàn)橹骶(xiàn)程是播放歌曲的。

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

        相關(guān)評(píng)論

        閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過(guò)難過(guò)
        • 5 囧
        • 3 圍觀圍觀
        • 2 無(wú)聊無(wú)聊

        熱門(mén)評(píng)論

        最新評(píng)論

        發(fā)表評(píng)論 查看所有評(píng)論(0)

        昵稱(chēng):
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)