<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ### 1.背景 歌詞是音樂軟件必備的,沒有它的存在就感覺少點什么,故實現了歌曲歌詞的顯示,使用LrcView實現,當然是在GitHub上找到的,是一個自定義View : LrcView 地址 :?[https://github.com/ChanWong21/LrcView](https://github.com/ChanWong21/LrcView) 效果預覽 :? ? ? 現在說說我使用過程中對它的不足之處做一下總結: ? ?(1)只能加載本地asserts文件夾中的lrc文件,不能請求網絡上的歌詞; ? ?(2)不能設置當前播放到得時間,也就不能顯示當前時間的歌詞,只可以順序播放; ? ?(3)當播放完畢后,重新播放一直停留在最后(因為時間是最后,永遠大于當前播放的時間); ? ?(4)沒事回調事件,無法判斷有沒有歌詞存在; ? ? ? 下面我將一一解決; ? # 2.歌詞LrcView實現 ### ? ? (1)實現 attr.xml? ~~~ <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="LrcView"> <attr name="textSize" format="dimension" /> <attr name="dividerHeight" format="dimension" /> <attr name="normalTextColor" format="reference|color" /> <attr name="currentTextColor" format="reference|color" /> <attr name="animationDuration" format="integer" /> </declare-styleable> </resources> ~~~ ### ? ? (2)LrcView實現 ? ? ? ? ? ? ? ? ? 歌詞的加載也是使用了上篇中的網絡加載模塊,可以輕松的實現數據請求; ? ? ? ? ? ? ? ? ??[Android實戰 - 音心播放器 (MusicActivity-音樂播放頁面界面實現,網絡模塊實現)](http://blog.csdn.net/lablenet/article/details/50324913) ? ? ? ? ? ? ? ? ? 幾個重要的方法 說明: ? ? ? ? ? ? ? ? ? ?onDraw() : 繪制當前顯示的歌詞; ? ? ? ? ? ? ? ? ? ?updateTime() : 外部調用,切換歌詞; ? ? ? ? ? ? ? ? ? ?parseLine() : 解析歌詞的每一行; ? ? ? ? ? ? ? ? ? ?lrcViewToMusicActivity 對象 : 回調事件,MusicActivity 實現該接口; ? ? ? ? ? ? ? ? ? ?LrcPlayToEnd : LrcView實現該接口,為了使得MusicActivity告訴LrcView,播放完畢,重新初始化LrcView,其實就是講當前時間改為0,非最大值; ~~~ package cn.labelnet.ui; import java.io.BufferedReader; import java.io.InputStreamReader; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.View; import cn.labelnet.event.LrcPlayToEnd; import cn.labelnet.event.LrcViewToMusicActivity; import cn.labelnet.maskmusic.R; import cn.labelnet.net.MusicAsyncGetUrl; import cn.labelnet.net.MusicAsyncHandlerGetLrc; import cn.labelnet.net.MusicRequest; /** * LrcView */ public class LrcView extends View implements MusicAsyncGetUrl, LrcPlayToEnd { private static final String TAG = LrcView.class.getSimpleName(); private static final int MSG_NEW_LINE = 0; private List<Long> mLrcTimes; private List<String> mLrcTexts; private LrcHandler mHandler; private Paint mNormalPaint; private Paint mCurrentPaint; private float mTextSize; private float mDividerHeight; private long mAnimationDuration; private long mNextTime = 0l; private int mCurrentLine = 0; private float mAnimOffset; private boolean mIsEnd = false; // 網絡 private MusicAsyncHandlerGetLrc musicAsyncHandlerGetLrc; private MusicRequest musicRequest; // 回調事件 private LrcViewToMusicActivity lrcViewToMusicActivity; private boolean isLrc = false; public void setLrcViewToMusicActivity( LrcViewToMusicActivity lrcViewToMusicActivity) { this.lrcViewToMusicActivity = lrcViewToMusicActivity; } private String songId = 001 + ""; public LrcView(Context context) { this(context, null); } public LrcView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } /** * 初始化 * * @param attrs * attrs */ private void init(AttributeSet attrs) { TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.LrcView); mTextSize = ta.getDimension(R.styleable.LrcView_textSize, 48.0f); mDividerHeight = ta.getDimension(R.styleable.LrcView_dividerHeight, 72.0f); mAnimationDuration = ta.getInt(R.styleable.LrcView_animationDuration, 1000); mAnimationDuration = mAnimationDuration < 0 ? 1000 : mAnimationDuration; // int normalColor = ta.getColor(R.color.app_color_whrit, // 0xffffffff); // int currentColor = ta.getColor(R.color.app_color, // 0xffff4081); ta.recycle(); mLrcTimes = new ArrayList<Long>(); mLrcTexts = new ArrayList<String>(); WeakReference<LrcView> lrcViewRef = new WeakReference<LrcView>(this); mHandler = new LrcHandler(lrcViewRef); mNormalPaint = new Paint(); mCurrentPaint = new Paint(); mNormalPaint.setColor(Color.WHITE); mNormalPaint.setTextSize(mTextSize); mCurrentPaint.setColor(Color.RED); mCurrentPaint.setTextSize(mTextSize); // 設置網絡監聽 musicAsyncHandlerGetLrc = new MusicAsyncHandlerGetLrc(); musicAsyncHandlerGetLrc.setMusicasyncGetUrl(this); musicRequest = new MusicRequest(); musicRequest.setMusicAsyncHandler(musicAsyncHandlerGetLrc); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mLrcTimes.isEmpty() || mLrcTexts.isEmpty()) { return; } // 中心Y坐標 float centerY = getHeight() / 2 + mTextSize / 2 + mAnimOffset; // 畫當前行 String currStr = mLrcTexts.get(mCurrentLine); float currX = (getWidth() - mCurrentPaint.measureText(currStr)) / 2; canvas.drawText(currStr, currX, centerY, mCurrentPaint); // 畫當前行上面的 for (int i = mCurrentLine - 1; i >= 0; i--) { String upStr = mLrcTexts.get(i); float upX = (getWidth() - mNormalPaint.measureText(upStr)) / 2; float upY = centerY - (mTextSize + mDividerHeight) * (mCurrentLine - i); canvas.drawText(upStr, upX, upY, mNormalPaint); } // 畫當前行下面的 for (int i = mCurrentLine + 1; i < mLrcTimes.size(); i++) { String downStr = mLrcTexts.get(i); float downX = (getWidth() - mNormalPaint.measureText(downStr)) / 2; float downY = centerY + (mTextSize + mDividerHeight) * (i - mCurrentLine); canvas.drawText(downStr, downX, downY, mNormalPaint); } } /** * 加載歌詞文件 * * @param lrcName * assets下的歌詞文件名 * @throws Exception */ public void loadLrc(String lrcName) throws Exception { mLrcTexts.clear(); mLrcTimes.clear(); BufferedReader br = new BufferedReader(new InputStreamReader( getResources().getAssets().open(lrcName))); String line; while ((line = br.readLine()) != null) { String[] arr = parseLine(line); if (arr != null) { mLrcTimes.add(Long.parseLong(arr[0])); mLrcTexts.add(arr[1]); } } br.close(); } /** * 加載歌詞文件 * * @param isr * @throws Exception */ public void loadLrcByUrl(String songid) throws Exception { if (!songId.equals(songid)) { mLrcTexts.clear(); mLrcTimes.clear(); mNextTime = 0; mCurrentLine = 0; mIsEnd = false; musicRequest.requestStringLrcData(songid); this.songId = songid; } } /** * 更新進度 * * @param time * 當前時間 */ public synchronized void updateTime(long time) { // 避免重復繪制 if (time < mNextTime || mIsEnd) { return; } for (int i = 0; i < mLrcTimes.size(); i++) { if (mLrcTimes.get(i) > time) { Log.i(TAG, "newline ..."); mNextTime = mLrcTimes.get(i); mCurrentLine = i < 1 ? 0 : i - 1; // 屬性動畫只能在主線程使用,因此用Handler轉發操作 mHandler.sendEmptyMessage(MSG_NEW_LINE); break; } else if (i == mLrcTimes.size() - 1) { // 最后一行 Log.i(TAG, "end ..."); mCurrentLine = mLrcTimes.size() - 1; mIsEnd = true; // 屬性動畫只能在主線程使用,因此用Handler轉發操作 mHandler.sendEmptyMessage(MSG_NEW_LINE); break; } } } /** * 解析一行 * * @param line * [00:10.61]走過了人來人往 * @return {10610, 走過了人來人往} */ private String[] parseLine(String line) { Matcher matcher = Pattern.compile("\\[(\\d)+:(\\d)+(\\.)(\\d+)\\].+") .matcher(line); if (!matcher.matches()) { Log.e(TAG, line); return null; } line = line.replaceAll("\\[", ""); String[] result = line.split("\\]"); result[0] = parseTime(result[0]); return result; } /** * 解析時間 * * @param time * 00:10.61 * @return long */ private String parseTime(String time) { time = time.replaceAll(":", "\\."); String[] times = time.split("\\."); long l = 0l; try { long min = Long.parseLong(times[0]); long sec = Long.parseLong(times[1]); long mil = Long.parseLong(times[2]); l = min * 60 * 1000 + sec * 1000 + mil * 10; } catch (NumberFormatException e) { e.printStackTrace(); } return String.valueOf(l); } /** * 換行動畫 Note:屬性動畫只能在主線程使用 */ private void newLineAnim() { ValueAnimator animator = ValueAnimator.ofFloat(mTextSize + mDividerHeight, 0.0f); animator.setDuration(mAnimationDuration); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAnimOffset = (Float) animation.getAnimatedValue(); invalidate(); } }); animator.start(); } private static class LrcHandler extends Handler { private WeakReference<LrcView> mLrcViewRef; public LrcHandler(WeakReference<LrcView> lrcViewRef) { mLrcViewRef = lrcViewRef; } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_NEW_LINE: LrcView lrcView = mLrcViewRef.get(); if (lrcView != null) { lrcView.newLineAnim(); } break; } super.handleMessage(msg); } } @Override public void getSongImageURL(String songLrc) { // 網絡請求成功歌詞 // Log.d("MaskMusic", songLrc); parseSongLrc(songLrc); } private void parseSongLrc(String songLrc) { Log.d("MaskMusic", songLrc); String[] strs = songLrc.split("\\["); for (String line : strs) { line = ("[" + line).replace(":", ":").replace(".", ".") .replace(" ", "").replace(" ", " ") .replace("-", "-").replace("(", "") .replace(")", "").replace("&", "") .replace(";", "").replace("'", "").replace("",""); String[] arr = parseLine(line); if (arr != null) { mLrcTimes.add(Long.parseLong(arr[0])); mLrcTexts.add(arr[1]); } // Log.d("MaskMusic", line); } // 回調判斷有沒有歌詞 if (mLrcTexts.size() > 0 && mLrcTimes.size() > 0) { isLrc = true; } lrcViewToMusicActivity.LrcViewIsLrc(isLrc); } @Override public void playToEnd() { // 播放完畢,進行初始化 // Log.d("MaskMusic", "playToEnd : 播放完畢"); mNextTime = 0; mCurrentLine = 0; mIsEnd = false; updateTime(mNextTime); } @Override public void playToPause(final long mt) { Log.d("MaskMusic", "mNextTime CurrentTime : " + mt); mHandler.postDelayed(new Runnable() { @Override public void run() { System.out.println("執行了"); if (mLrcTexts.size() > 0 && mLrcTimes.size() > 0) { for (int i = 0; i < mLrcTimes.size() - 1; i++) { if (mt >= mLrcTimes.get(i) && mt <= mLrcTimes.get(i + 1)) { Log.d("MaskMusic", mt + " 毫秒的歌詞為 " + mLrcTexts.get(i)); mNextTime = mLrcTimes.get(i); mCurrentLine = i; updateTime(mNextTime); } } }else{ lrcViewToMusicActivity.LrcViewIsLrc(false); } Log.d("MaskMusic", "playToPause over"); } }, 2000); // 遇到問題 ,從MusicService 的 時間,很難與 集合中的時間匹配成功! } } ~~~ ### ? ? (3)回調事件1 (LrcView-MusicActivity) ? ? ? ? ? ? ?作用是給MusicActivity 回調,判斷是否有歌詞 ~~~ public interface LrcViewToMusicActivity { /** * LrcView的自定義事件,給 */ /** * * 1.判斷是否有歌詞 * 2.在進行初始化成功后,2s之內沒有加載到歌詞就顯示提示 * @param isLrc,是否有歌詞 */ void LrcViewIsLrc(boolean isLrc); } ~~~ ? ?? ### ? ? ?(4)回調事件2(MusicActivity - LrcView) ~~~ <pre name="code" class="java">/** * 接口實現意圖 :LrcView實現此接口,后在MusciActivity中,使用其接口,將調用LrcView中實現的playToEnd()方法, * 進行歌詞初始化操作 */ public interface LrcPlayToEnd { /** * 播放到最后,回調初始化 歌詞顯示 */ void playToEnd(); /** * 暫停后,初始化節面時,將歌詞設置到當前時間位置 */ void playToPause(long mNextTime); } ~~~ ~~~ ~~~ # 3.Activity與LrcView控制實現 ### ? ?(1)一張圖看明白 ![](https://box.kancloud.cn/2016-04-19_5715764dde417.jpg) ### ? ? (2)LrcView - MusicActivity? ? ? ? ? ? ? ? 回調事件,一個接口作為目標的屬性,使用者實現這個接口,來使用,我們在前面已經使用了很多次,比如Fragment - > MainActivity 通信過程 等; ? ? ? ? ? ? ? 在這里,接口 LrcViewToMusicActivity ,作為LrcView的屬性,進行回調出是否有歌詞;在MusicActivity中實現該接口,使用有沒有歌詞;在初始化LrcView的時候,setLrcViewToMusicActivity(this)就可以實現; ### ? ? (3)MusicActivity -> LrcView? ? ? ? ? ? ? ?作用 : 判斷歌曲有沒接觸,當結束后出發,LrcView進行初始化操作; ? ? ? ? ? ? ?實現過程: ? ? ? ? ? ? ? ? 1)使得LrcView實現LrcViewToEnd 接口; ? ? ? ? ? ? ? ? 2)在MusicActivity中使用LrcViewtoEnd ,作為屬性使用,初始化的時候直接將 ~~~ lrc = (LrcView) findViewById(R.id.lrc); //初始化接口,多態實現 LrcViewToEnd lrcplaytoend = lrc; ~~~ # 4.總結 ? ? 在使用過程中,最糾結的就是當音樂播放完畢的時候進行初始化歌詞了,當初使用了很多方法,比如觀察者模式,應該也是可以解決的,但無意當中,想到了這個簡單的方法,可以實現這個功能,得益于面向對象的多態的再次學習;所以基礎不能忘記,要時常學習,則會事半功倍。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看