TicktockMusic 音樂播放器項目相關文章匯總:
- Clean Architecture 架構:http://www.reibang.com/p/15ea0fecb61d
- 開源庫封裝:http://www.reibang.com/p/1645b81dc994
- 自定義播放暫停按鈕:http://www.reibang.com/p/74f38e9b16fc
- 自定義歌詞控件:http://www.reibang.com/p/ab735509cc74
最近想寫個音樂播放器尖奔,偶然看到輕聽這款播放器的播放和暫停按鈕橡娄,在切換過程中的動畫很是吸引我勺阐。本著造輪子(其實是 github 上邊沒找到)的想法,就花了點時間擼出來了這個效果政恍。
效果就是下邊這個樣子:
下邊說下實現(xiàn)方法,中間也踩了一些坑达传。
測量及初始化
首先要確實View的寬高篙耗,在這里由于是圓形按鈕,所以設置寬高相等宪赶,onMeasure()方法中設置下即可:
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
switch (widthMode) {
case MeasureSpec.EXACTLY:
mWidth = mHeight = Math.min(mWidth, mHeight);
setMeasuredDimension(mWidth, mHeight);
break;
case MeasureSpec.AT_MOST:
float density = getResources().getDisplayMetrics().density;
mWidth = mHeight = (int) (50 * density); //默認50dp
setMeasuredDimension(mWidth, mHeight);
break;
}
然后畫出底部的圓形
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mPaint);
計算Path
1宗弯、初始化完畢后,怎么實現(xiàn)兩個豎條到一個三角形的過渡呢搂妻?這里首先想到的就是自定義 View 常用的 drawPath 方法蒙保,拋開動畫不談,整個 View 變化過程其實就是兩個矩形變成兩個直角三角形的過程欲主。
就是這個樣子追他。知道大體的思路,怎么搞呢岛蚤,當然是開車了邑狸。
就是 canvas.drawPath();
首先計算暫停時兩個矩形的各個坐標位置:
float distance = mGapWidth; //暫停時左右兩邊矩形距離
float barWidth = mRectWidth / 2 - distance / 2; //一個矩形的寬度
float leftLeftTop = barWidth; //左邊矩形左上角
float rightLeftTop = barWidth + distance; //右邊矩形左上角
float rightRightTop = 2 * barWidth + distance; //右邊矩形右上角
float rightRightBottom = rightRightTop; //右邊矩形右下角
bottom 的話直接加上矩形的高度即可。
mLeftPath.moveTo(0, 0);
mLeftPath.lineTo(leftLeftTop, mRectHeight);
mLeftPath.lineTo(barWidth, mRectHeight);
mLeftPath.lineTo(barWidth, 0);
mLeftPath.close();
mRightPath.moveTo(rightLeftTop, 0);
mRightPath.lineTo(rightLeftTop, mRectHeight);
mRightPath.lineTo(rightRightBottom, mRectHeight);
mRightPath.lineTo(rightRightTop, 0);
mRightPath.close();
這樣兩個豎條就出來了涤妒。
2单雾、在一開始寫的時候就寫了這么多計算的方法,但是這時候矩形的邊角會超出 View 的范圍她紫,所以后來計算了一波位置:
如上圖所示硅堆,這樣就需要再更改一些參數(shù):
首先定義出來這個矩形,計算下寬高:
float space = (float) (mRadius / Math.sqrt(2));
mRectLT = (int) (mRadius - space);
int rectRB = (int) (mRadius + space);
mRect.top = mRectLT;
mRect.bottom = rectRB;
mRect.left = mRectLT;
mRect.right = rectRB;
然后只用在 確定 path 的路線時更改下坐標就可以了:
mLeftPath.moveTo(mRectLT, mRectLT);
mLeftPath.lineTo(leftLeftTop + mRectLT, mRectHeight + mRectLT);
mLeftPath.lineTo(barWidth + mRectLT, mRectHeight + mRectLT);
mLeftPath.lineTo(barWidth + mRectLT, mRectLT);
mLeftPath.close();
mRightPath.moveTo(rightLeftTop + mRectLT, mRectLT);
mRightPath.lineTo(rightLeftTop + mRectLT, mRectHeight + mRectLT);
mRightPath.lineTo(rightRightBottom + mRectLT, mRectHeight + mRectLT);
mRightPath.lineTo(rightRightTop + mRectLT, mRectLT);
mRightPath.close();
這時候畫出來兩個 Path贿讹,暫停按鈕就完美的呈現(xiàn)了:
canvas.drawPath(mLeftPath, mPaint);
canvas.drawPath(mRightPath, mPaint);
如下圖這樣:
動畫實現(xiàn)
畫完暫停按鈕后渐逃,怎么讓他動畫變成三角形呢?一開始我想根據(jù)一些寬高的屬性來指定動畫的變化值民褂,然后更新過程中再畫出來茄菊,但是計算過程中發(fā)現(xiàn)涉及動畫的矩形寬度都是從原始的大小到0過渡的疯潭,那統(tǒng)一的使用一個參數(shù)確定會不會更好點呢?當然會了面殖,從1倍到0變化即可竖哩。
這時候就可以設置動畫屬性了:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0 , 1);
valueAnimator.setDuration(200);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mProgress = (float) animation.getAnimatedValue();
invalidate();
}
});
然后根據(jù) progress 在更新View的過程中來更改矩形的寬高值:
float distance = mGapWidth * (1 - mProgress); //暫停時左右兩邊矩形距離
float barWidth = mRectWidth / 2 - distance / 2; //一個矩形的寬度
float leftLeftTop = barWidth * mProgress; //左邊矩形左上角
float rightLeftTop = barWidth + distance; //右邊矩形左上角
float rightRightTop = 2 * barWidth + distance; //右邊矩形右上角
float rightRightBottom = rightRightTop - barWidth * mProgress; //右邊矩形右下角
這樣便可以實現(xiàn)兩個矩形到三角形的過渡了,執(zhí)行動畫結束后便是這個樣子:
兩個矩形變成三角形之后脊僚,只需要畫布旋轉(zhuǎn)一下相叁,兩個暫停按鈕到播放按鈕的動畫已經(jīng)可以執(zhí)行了:
canvas.rotate(rotation, mWidth / 2f, mHeight / 2f);
到這里基本上已經(jīng)結束了,但是寫完使用的時候總覺得位置有點不對勁辽幌,后來發(fā)現(xiàn)確實有問題:
如圖所示增淹,旋轉(zhuǎn)過后 A 和 C 本來是緊靠著圓周的,而 B 距離圓周還有一定的距離乌企。所以需要將其位移 x 的距離埠通,讓 OC 的長度等于 BO 的長度。此時圓心O也是三角形的外心逛犹。那么此時可以計算出OF的距離端辱,公式如下:
√(( r / √2 ) ^ 2 + OF ^ 2) = √2 * r - OF
得出 OF 的長度為: 3 * √2 * r / 8
那么原矩形寬度的一半減去 OF 的值即為右移的距離,計算可得虽画,右移的距離為 √2 * r / 8 用 Java 表示即
radius * Math.sqrt(2) / 8f
換算為矩形的高度即
mRectHeight / 8f
然后在畫布位移一下即可:
canvas.translate((float) (mRectHeight / 8f * mProgress), 0);
總結
上邊幾個步驟寫完舞蔽,整體效果已經(jīng)實現(xiàn)了。后來又設置了一系列自定義的參數(shù)方便使用:
<declare-styleable name="PlayPauseView">
<attr name="bg_color" format="color"/>
<attr name="btn_color" format="color"/>
<attr name="gap_width" format="float"/>
<attr name="space_padding" format="float"/>
<attr name="anim_duration" format="integer"/>
<attr name="anim_direction">
<enum name="positive" value="1"/>
<enum name="negative" value="2"/>
</attr>
</declare-styleable>
所有代碼都已經(jīng)上傳到 我的Github 上邊了码撰,點擊可查看渗柿,希望提出問題相互討論,隨便給個 Star 再好不過了脖岛。
有問題交流可加QQ群 661614986 朵栖,歡迎討論。