一.概述
項目中設計到歌詞顯示的問題,這一塊之前沒有涉及過,只是套用過一個開源的項目,效果還行,於是想到拿來稍作修改,以適應項目需求.
二.歌詞控件
先來看下這個自定義控件寫的歌詞控件吧:
public class LrcView extends View implements ILrcView { /** * 所有的歌詞 ***/ private List<LrcRow> mLrcRows; /** * 無歌詞數據的時候 顯示的默認文字 **/ private static final String DEFAULT_TEXT = "*暫未獲取到歌詞*"; /** * 默認文字的字體大小 **/ private static final float SIZE_FOR_DEFAULT_TEXT = CommonUtils.dip2px(MyApplication.getContext(), 28); /** * 畫高亮歌詞的畫筆 ***/ private Paint mPaintForHighLightLrc; /** * 高亮歌詞的默認字體大小 ***/ private static final float DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC = CommonUtils.dip2px(MyApplication.getContext(), 32); /** * 高亮歌詞當前的字體大小 ***/ private float mCurSizeForHightLightLrc = DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC; /** * 高亮歌詞的默認字體顏色 **/ private static final int DEFAULT_COLOR_FOR_HIGHT_LIGHT_LRC = 0xffffffff; /** * 高亮歌詞當前的字體顏色 **/ private int mCurColorForHightLightLrc = DEFAULT_COLOR_FOR_HIGHT_LIGHT_LRC; /** * 畫其他歌詞的畫筆 ***/ private Paint mPaintForOtherLrc; /** * 其他歌詞的默認字體大小 ***/ private static final float DEFAULT_SIZE_FOR_OTHER_LRC = CommonUtils.dip2px(MyApplication.getContext(), 28); /** * 其他歌詞當前的字體大小 ***/ private float mCurSizeForOtherLrc = DEFAULT_SIZE_FOR_OTHER_LRC; /** * 其他歌詞的默認字體顏色 **/ private static final int DEFAULT_COLOR_FOR_OTHER_LRC = 0x66ffffff; /** * 其他歌詞當前的字體顏色 **/ private int mCurColorForOtherLrc = DEFAULT_COLOR_FOR_OTHER_LRC; /** * 畫時間線的畫筆 ***/ private Paint mPaintForTimeLine; /*** * 時間線的顏色 **/ private static final int COLOR_FOR_TIME_LINE = 0xff999999; /** * 時間文字大小 **/ private static final int SIZE_FOR_TIME = CommonUtils.dip2px(MyApplication.getContext(), 12); /** * 是否畫時間線 **/ private boolean mIsDrawTimeLine = false; /** * 歌詞間默認的行距 **/ private static final float DEFAULT_PADDING = CommonUtils.dip2px(MyApplication.getContext(), 17); /** * 歌詞當前的行距 **/ private float mCurPadding = DEFAULT_PADDING; /** * 歌詞的最大縮放比例 **/ public static final float MAX_SCALING_FACTOR = 1.5f; /** * 歌詞的最小縮放比例 **/ public static final float MIN_SCALING_FACTOR = 0.5f; /** * 默認縮放比例 **/ private static final float DEFAULT_SCALING_FACTOR = 1.0f; /** * 歌詞的當前縮放比例 **/ private float mCurScalingFactor = DEFAULT_SCALING_FACTOR; /** * 實現歌詞豎直方向平滑滾動的輔助對象 **/ private Scroller mScroller; /*** * 移動一句歌詞的持續時間 **/ private static final int DURATION_FOR_LRC_SCROLL = 500; /*** * 停止觸摸時 如果View需要滾動 時的持續時間 **/ private static final int DURATION_FOR_ACTION_UP = 400; /** * 控制文字縮放的因子 **/ private float mCurFraction = 0; private int mTouchSlop; private Bitmap arrowBitmap; public LrcView(Context context) { super(context); init(context); } public LrcView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } /** * 初始化畫筆等 */ @Override public void init(Context context) { mScroller = new Scroller(getContext()); mPaintForHighLightLrc = new Paint(); // mPaintForHighLightLrc.setShadowLayer(5,0,0, Color.parseColor("#66ffffff")); mPaintForHighLightLrc.setColor(mCurColorForHightLightLrc); mPaintForHighLightLrc.setTextSize(mCurSizeForHightLightLrc); mPaintForHighLightLrc.setAntiAlias(true); mPaintForOtherLrc = new Paint(); mPaintForOtherLrc.setColor(mCurColorForOtherLrc); mPaintForOtherLrc.setTextSize(mCurSizeForOtherLrc); mPaintForOtherLrc.setAntiAlias(true); mPaintForTimeLine = new Paint(); mPaintForTimeLine.setColor(COLOR_FOR_TIME_LINE); mPaintForTimeLine.setTextSize(SIZE_FOR_TIME); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inDensity = 30; options.inTargetDensity = 30; arrowBitmap = BitmapFactory.decodeResource(context.getResources(), R.raw.lrc_arrow, options); } private int mTotleDrawRow; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mLrcRows == null || mLrcRows.size() == 0) { //畫默認的顯示文字 mPaintForOtherLrc.setTextSize(SIZE_FOR_DEFAULT_TEXT); float textWidth = mPaintForOtherLrc.measureText(DEFAULT_TEXT); float textX = (getWidth() - textWidth) / 2; canvas.drawText(DEFAULT_TEXT, textX, getHeight() / 2, mPaintForOtherLrc); return; } if (mTotleDrawRow == 0) { //初始化將要繪制的歌詞行數 mTotleDrawRow = (int) (getHeight() / (mCurSizeForOtherLrc + mCurPadding)) + 4; } //因為不需要將所有歌詞畫出來 int minRaw = mCurRow - (mTotleDrawRow - 1) / 2; int maxRaw = mCurRow + (mTotleDrawRow - 1) / 2; minRaw = Math.max(minRaw, 0); //處理上邊界 maxRaw = Math.min(maxRaw, mLrcRows.size() - 1); //處理下邊界 //實現漸變的最大歌詞行數 int count = Math.max(maxRaw - mCurRow, mCurRow - minRaw); if (count == 0) { return; } //兩行歌詞間字體顏色變化的透明度 int alpha = (0xFF - 0x11) / count; //畫出來的第一行歌詞的y坐標 float rowY = getHeight() / 2 + minRaw * (mCurSizeForOtherLrc + mCurPadding); for (int i = minRaw; i <= maxRaw; i++) { if (i == mCurRow) {//畫高亮歌詞 //因為有縮放效果,所有需要動態設置歌詞的字體大小 float textSize = mCurSizeForOtherLrc + (mCurSizeForHightLightLrc - mCurSizeForOtherLrc) * mCurFraction; mPaintForHighLightLrc.setTextSize(textSize); String text = mLrcRows.get(i).getContent();//獲取到高亮歌詞 float textWidth = mPaintForHighLightLrc.measureText(text);//用畫筆測量歌詞的寬度 if (textWidth > getWidth()) { //如果歌詞寬度大於view的寬,則需要動態設置歌詞的起始x坐標,以實現水平滾動 canvas.drawText(text, mCurTextXForHighLightLrc, rowY, mPaintForHighLightLrc); } else { //如果歌詞寬度小於view的寬,則讓歌詞居中顯示 float textX = (getWidth() - textWidth) / 2; canvas.drawText(text, textX, rowY, mPaintForHighLightLrc); } } else { if (i == mLastRow) {//畫高亮歌詞的上一句 //因為有縮放效果,所有需要動態設置歌詞的字體大小 float textSize = mCurSizeForHightLightLrc - (mCurSizeForHightLightLrc - mCurSizeForOtherLrc) * mCurFraction; mPaintForOtherLrc.setTextSize(textSize); } else {//畫其他的歌詞 mPaintForOtherLrc.setTextSize(mCurSizeForOtherLrc); } String text = mLrcRows.get(i).getContent(); float textWidth = mPaintForOtherLrc.measureText(text); float textX = (getWidth() - textWidth) / 2; //如果計算出的textX為負數,將textX置為0(實現:如果歌詞寬大於view寬,則居左顯示,否則居中顯示) textX = Math.max(textX, 0); //實現顏色漸變 從0xFFFFFFFF 逐漸變為 0x11FFFFFF(顏色還是白色,只是透明度變化) int curAlpha = 255 - (Math.abs(i - mCurRow) - 1) * alpha; //求出當前歌詞顏色的透明度 //mPaintForOtherLrc.setColor(0x1000000*curAlpha+0xffffff); canvas.drawText(text, textX, rowY, mPaintForOtherLrc); } //計算出下一行歌詞繪制的y坐標 rowY += mCurSizeForOtherLrc + mCurPadding; } //畫時間線和時間 if (mIsDrawTimeLine) { float y = getHeight() / 2 + getScrollY(); float x = getWidth(); canvas.drawBitmap(arrowBitmap, -20, y - 41, null); canvas.drawText(mLrcRows.get(mCurRow).getTimeStr().substring(0, 5), x - 105, y + 13, mPaintForTimeLine); canvas.drawLine(60, y, getWidth() - 110, y, mPaintForTimeLine); } } /** * 是否可拖動歌詞 **/ private boolean canDrag = false; /** * 事件的第一次的y坐標 **/ private float firstY; /** * 事件的上一次的y坐標 **/ private float lastY; private float lastX; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: firstY = event.getRawY(); lastX = event.getRawX(); break; case MotionEvent.ACTION_MOVE: if (mLrcRows == null || mLrcRows.size() == 0) { return false; } if (!canDrag) { if (Math.abs(event.getRawY() - firstY) > mTouchSlop && Math.abs(event.getRawY() - firstY) > Math.abs(event.getRawX() - lastX)) { canDrag = true; mIsDrawTimeLine = true; mScroller.forceFinished(true); stopScrollLrc(); mCurFraction = 1; } lastY = event.getRawY(); } if (canDrag) { float offset = event.getRawY() - lastY;//偏移量 if (getScrollY() - offset < 0) { if (offset > 0) { offset = offset / 3; } } else if (getScrollY() - offset > mLrcRows.size() * (mCurSizeForOtherLrc + mCurPadding) - mCurPadding) { if (offset < 0) { offset = offset / 3; } } scrollBy(getScrollX(), -(int) offset); lastY = event.getRawY(); int currentRow = (int) (getScrollY() / (mCurSizeForOtherLrc + mCurPadding)); currentRow = Math.min(currentRow, mLrcRows.size() - 1); currentRow = Math.max(currentRow, 0); seekTo(mLrcRows.get(currentRow).getTime(), false, false); return true; } lastY = event.getRawY(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (!canDrag) { if (onLrcClickListener != null) { onLrcClickListener.onClick(); } } else { if (onSeekToListener != null && mCurRow != -1) { onSeekToListener.onSeekTo(mLrcRows.get(mCurRow).getTime()); } if (getScrollY() < 0) { smoothScrollTo(0, DURATION_FOR_ACTION_UP); } else if (getScrollY() > mLrcRows.size() * (mCurSizeForOtherLrc + mCurPadding) - mCurPadding) { smoothScrollTo((int) (mLrcRows.size() * (mCurSizeForOtherLrc + mCurPadding) - mCurPadding), DURATION_FOR_ACTION_UP); } canDrag = false; mIsDrawTimeLine = false; invalidate(); } break; } return true; } /** * 為LrcView設置歌詞List集合數據 */ @Override public void setLrcRows(List<LrcRow> lrcRows) { reset(); this.mLrcRows = lrcRows; invalidate(); } /** * 當前高亮歌詞的行號 **/ private int mCurRow = -1; /** * 上一次的高亮歌詞的行號 **/ private int mLastRow = -1; @Override public void seekTo(int progress, boolean fromSeekBar, boolean fromSeekBarByUser) { if (mLrcRows == null || mLrcRows.size() == 0) { return; } //如果是由seekbar的進度改變觸發 並且這時候處於拖動狀態,則返回 if (fromSeekBar && canDrag) { return; } for (int i = mLrcRows.size() - 1; i >= 0; i--) { if (progress >= mLrcRows.get(i).getTime()) { if (mCurRow != i) { mLastRow = mCurRow; mCurRow = i; log("mCurRow=i=" + mCurRow); if (fromSeekBarByUser) { if (!mScroller.isFinished()) { mScroller.forceFinished(true); } scrollTo(getScrollX(), (int) (mCurRow * (mCurSizeForOtherLrc + mCurPadding))); } else { smoothScrollTo((int) (mCurRow * (mCurSizeForOtherLrc + mCurPadding)), DURATION_FOR_LRC_SCROLL); } //如果高亮歌詞的寬度大於View的寬,就需要開啟屬性動畫,讓它水平滾動 float textWidth = mPaintForHighLightLrc.measureText(mLrcRows.get(mCurRow).getContent()); log("textWidth=" + textWidth + "getWidth()=" + getWidth()); if (textWidth > getWidth()) { if (fromSeekBarByUser) { mScroller.forceFinished(true); } log("開始水平滾動歌詞:" + mLrcRows.get(mCurRow).getContent()); startScrollLrc(getWidth() - textWidth, (long) (mLrcRows.get(mCurRow).getTotalTime() * 0.6)); } invalidate(); } break; } } } /** * 控制歌詞水平滾動的屬性動畫 ***/ private ValueAnimator mAnimator; /** * 開始水平滾動歌詞 * * @param endX 歌詞第一個字的最終的x坐標 * @param duration 滾動的持續時間 */ private void startScrollLrc(float endX, long duration) { if (mAnimator == null) { mAnimator = ValueAnimator.ofFloat(0, endX); mAnimator.addUpdateListener(updateListener); } else { mCurTextXForHighLightLrc = 0; mAnimator.cancel(); mAnimator.setFloatValues(0, endX); } mAnimator.setDuration(duration); // mAnimator.setStartDelay((long) (duration * 0.2)); //延遲執行屬性動畫 mAnimator.start(); } /** * 停止歌詞的滾動 */ private void stopScrollLrc() { if (mAnimator != null) { mAnimator.cancel(); } mCurTextXForHighLightLrc = 0; } /** * 高亮歌詞當前的其實x軸繪制坐標 **/ private float mCurTextXForHighLightLrc; /*** * 監聽屬性動畫的數值值的改變 */ AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurTextXForHighLightLrc = (Float) animation.getAnimatedValue(); log("mCurTextXForHighLightLrc=" + mCurTextXForHighLightLrc); invalidate(); } }; /** * 設置歌詞的縮放比例 */ @Override public void setLrcScalingFactor(float scalingFactor) { mCurScalingFactor = scalingFactor; mCurSizeForHightLightLrc = DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC * mCurScalingFactor; mCurSizeForOtherLrc = DEFAULT_SIZE_FOR_OTHER_LRC * mCurScalingFactor; mCurPadding = DEFAULT_PADDING * mCurScalingFactor; mTotleDrawRow = (int) (getHeight() / (mCurSizeForOtherLrc + mCurPadding)) + 3; log("mRowTotal=" + mTotleDrawRow); scrollTo(getScrollX(), (int) (mCurRow * (mCurSizeForOtherLrc + mCurPadding))); invalidate(); mScroller.forceFinished(true); } /** * 重置 */ @Override public void reset() { if (!mScroller.isFinished()) { mScroller.forceFinished(true); } mLrcRows = null; scrollTo(getScrollX(), 0); invalidate(); } /** * 平滑的移動到某處 * * @param dstY */ private void smoothScrollTo(int dstY, int duration) { int oldScrollY = getScrollY(); int offset = dstY - oldScrollY; mScroller.startScroll(getScrollX(), oldScrollY, getScrollX(), offset, duration); invalidate(); } @Override public void computeScroll() { if (!mScroller.isFinished()) { if (mScroller.computeScrollOffset()) { int oldY = getScrollY(); int y = mScroller.getCurrY(); if (oldY != y && !canDrag) { scrollTo(getScrollX(), y); } mCurFraction = mScroller.timePassed() * 3f / DURATION_FOR_LRC_SCROLL; mCurFraction = Math.min(mCurFraction, 1F); invalidate(); } } } /** * 返回當前的歌詞縮放比例 * * @return */ public float getmCurScalingFactor() { return mCurScalingFactor; } private OnSeekToListener onSeekToListener; public void setOnSeekToListener(OnSeekToListener onSeekToListener) { this.onSeekToListener = onSeekToListener; } public interface OnSeekToListener { void onSeekTo(int progress); } private OnLrcClickListener onLrcClickListener; public void setOnLrcClickListener(OnLrcClickListener onLrcClickListener) { this.onLrcClickListener = onLrcClickListener; } public interface OnLrcClickListener { void onClick(); } public void log(Object o) { Log.d("LrcView", o + ""); } }
* 在ViewGroup里面 scrollTo,scrollBy方法移動的是子View * 在View里面scrollTo,scrollBy方法移動的是View里面繪制的內容 * 要點: * 1:歌詞的上下平移用什么實現? * 用Scroller實現,Scroller只是一個工具而已, * 真正實現滾動效果的還是View的scrollTo方法 * 2:歌詞的水平滾動怎么實現? * 通過屬性動畫ValueAnimator控制高亮歌詞繪制的x軸起始坐標
歌詞與播放進度聯動,只需要調用seekTo方法,原理是拿播放進度和歌詞每一行前的時間作比較,只要播放進度超前,那么就滾動到歌詞的相應時間上顯示,理論上,只要歌詞文本的時間是精確的,那么歌詞就會隨着
播放進度一直滾動.
觸摸滑動和點擊事件都是在onTouchEvent中處理的,原理是記錄手指按下和抬起這段時間內Y方向的偏移量,把這個偏移量與每行文字的高度作比較,看滾動到歌詞的哪個部分,然后再把播放進度調到對應的時間位置,這樣
就實現了歌詞進度與播放進度的完全綁定了.
當然,這些都是建立在歌詞解析完成,並且獲取到的前提之下,因此,歌詞解析也是很重要的部分.
三.歌詞解析
下載的歌詞文件,看過的都知道是一種時間刻度+歌詞字符串的形式;例如
這樣的,既然如此,那么只需要按"["和"]"來截取字符串就可以了.來看下歌詞解析類吧(歌詞解析不少網友都分享過):
public class DefaultLrcParser implements ILrcParser { private static final DefaultLrcParser istance = new DefaultLrcParser(); public static final DefaultLrcParser getIstance() { return istance; } private DefaultLrcParser() { } /*** * 將歌詞文件里面的字符串 解析成一個List<LrcRow> */ @Override public List<LrcRow> getLrcRows(String str) { if (TextUtils.isEmpty(str)) { return null; } BufferedReader br = new BufferedReader(new StringReader(str)); List<LrcRow> lrcRows = new ArrayList<>(); String lrcLine; try { while ((lrcLine = br.readLine()) != null) { List<LrcRow> rows = LrcRow.createRows(lrcLine); if (rows != null && rows.size() > 0) { lrcRows.addAll(rows); } } Collections.sort(lrcRows); int len = lrcRows.size(); for (int i = 0; i < len - 1; i++) { lrcRows.get(i).setTotalTime(lrcRows.get(i + 1).getTime() - lrcRows.get(i).getTime()); } lrcRows.get(len - 1).setTotalTime(5000); } catch (Exception e) { e.printStackTrace(); return null; } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return lrcRows; }
歌詞的實體類:
/** * 每行歌詞的實體類,實現了Comparable接口,方便List<LrcRow>的sort排序 * * @author Ligang 2014/8/19 */ public class LrcRow implements Comparable<LrcRow> { /** * 開始時間 為00:10:00 ***/ private String timeStr; /** * 開始時間 毫米數 00:10:00 為10000 **/ private int time; /** * 歌詞內容 **/ private String content; /** * 該行歌詞顯示的總時間 **/ private int totalTime; public long getTotalTime() { return totalTime; } public void setTotalTime(int totalTime) { this.totalTime = totalTime; } public String getTimeStr() { return timeStr; } public void setTimeStr(String timeStr) { this.timeStr = timeStr; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public LrcRow() { super(); } public LrcRow(String timeStr, int time, String content) { super(); this.timeStr = timeStr; this.time = time; this.content = content; } /** * 將歌詞文件中的某一行 解析成一個List<LrcRow> * 因為一行中可能包含了多個LrcRow對象 * 比如 [03:33.02][00:36.37]當鴿子不再象征和平 ,就包含了2個對象 * * @param lrcLine * @return */ public static final List<LrcRow> createRows(String lrcLine) { if (!lrcLine.startsWith("[")) { return null; } //最后一個"]" int lastIndexOfRightBracket = lrcLine.lastIndexOf("]"); //歌詞內容 String content = lrcLine.substring(lastIndexOfRightBracket + 1, lrcLine.length()); //截取出歌詞時間,並將"[" 和"]" 替換為"-" [offset:0] Log.e("歌詞","lrcLine=" + lrcLine); // -03:33.02--00:36.37- String times = lrcLine.substring(0, lastIndexOfRightBracket + 1).replace("[", "-").replace("]", "-"); String[] timesArray = times.split("-"); List<LrcRow> lrcRows = new ArrayList<LrcRow>(); for (String tem : timesArray) { if (TextUtils.isEmpty(tem.trim())) { continue; } // try { LrcRow lrcRow = new LrcRow(tem, formatTime(tem), content); lrcRows.add(lrcRow); } catch (Exception e) { Log.w("LrcRow", e.getMessage()); } } return lrcRows; } /**** * 把歌詞時間轉換為毫秒值 如 將00:10.00 轉為10000 * * @param timeStr * @return */ private static int formatTime(String timeStr) { timeStr = timeStr.replace('.', ':'); String[] times = timeStr.split(":"); return Integer.parseInt(times[0]) * 60 * 1000 + Integer.parseInt(times[1]) * 1000 + Integer.parseInt(times[2]); } @Override public int compareTo(LrcRow anotherLrcRow) { return (int) (this.time - anotherLrcRow.time); } @Override public String toString() { return "LrcRow [timeStr=" + timeStr + ", time=" + time + ", content=" + content + "]"; } }
解析成LrcRow后按照時間順序排列,得到的集合作為一篇歌詞的解析結果.
四.歌詞顯示
歌詞顯示的邏輯也有要注意的地方,下面畫了個簡圖
private class RequestLrc implements Runnable { private TrackEntity musicInfo; private boolean stop; RequestLrc(TrackEntity info) { this.musicInfo = info; } public void stop() { stop = true; } @Override public void run() { String url; if (musicInfo == null || musicInfo.getTrackId() == 0) { return; } String action = MUSIC_DETAIL + musicInfo.getTrackId(); AscHttpHelper helper = new AscHttpHelper(getContext()); url = helper.wrapUrl(action, null); Log.i(TAG, "請求url" + url); String resposeString = HttpUtil.getResposeString(url); Log.i(TAG, "請求結果" + resposeString); Gson gson = new Gson(); SongDetailBean mDetailSong = gson.fromJson(resposeString, SongDetailBean.class); if (mDetailSong == null || mDetailSong.data == null || mDetailSong.data.musicInfoList == null) { return; } if (!stop) { File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + LRC_PATH + musicInfo.getTrackId()); String lrc; try { lrc = HttpUtil.getResposeString(mDetailSong.data.musicInfoList.get(0).lyricUrl); if (!TextUtils.isEmpty(lrc)) { if (!file.exists()) { file.createNewFile(); } writeToFile(file, lrc); } } catch (Exception e) { e.printStackTrace(); mTryGetLrc.post(new Runnable() { @Override public void run() { mTryGetLrc.setVisibility(View.VISIBLE); mLrcView.reset(); } }); } } } } private synchronized void writeToFile(File file, String lrc) { FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(file); outputStream.write(lrc.getBytes()); } catch (Exception e) { e.printStackTrace(); mTryGetLrc.setVisibility(View.VISIBLE); mLrcView.reset(); } finally { if (outputStream != null) { try { outputStream.flush(); outputStream.close(); final List<LrcRow> list = getLrcRows(); mTryGetLrc.post(new Runnable() { @Override public void run() { if (list != null && list.size() > 0) { mTryGetLrc.setVisibility(View.INVISIBLE); mLrcView.setLrcRows(list); } } }); } catch (IOException e) { e.printStackTrace(); mTryGetLrc.setVisibility(View.VISIBLE); mLrcView.reset(); } } } }
五.體會與總結
千里之行,始於足下;任何看起來很棒的效果都是一行一行基礎代碼在發揮作用,他們相互關聯,卻又相互獨立.
這兩天來看,上面的歌詞顯示邏輯還是存在一些問題的,比如切換到下一首的時候,偶現顯示了上一首歌曲的歌詞.好的,我去修bug了.