自定義View:播放浆兰、暫停按鈕優(yōu)雅的過渡

TicktockMusic 音樂播放器項目相關文章匯總:

最近想寫個音樂播放器尖奔,偶然看到輕聽這款播放器的播放和暫停按鈕橡娄,在切換過程中的動畫很是吸引我勺阐。本著造輪子(其實是 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)確實有問題:

計算過程.png

如圖所示增淹,旋轉(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 朵栖,歡迎討論。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柴梆,一起剝皮案震驚了整個濱河市陨溅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绍在,老刑警劉巖门扇,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異偿渡,居然都是意外死亡臼寄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門溜宽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吉拳,“玉大人,你說我怎么就攤上這事适揉×粼埽” “怎么了煤惩?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稼跳。 經(jīng)常有香客問我盟庞,道長吃沪,這世上最難降的妖魔是什么汤善? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮票彪,結果婚禮上红淡,老公的妹妹穿的比我還像新娘。我一直安慰自己降铸,他們只是感情好在旱,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著推掸,像睡著了一般桶蝎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谅畅,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天登渣,我揣著相機與錄音,去河邊找鬼毡泻。 笑死胜茧,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的仇味。 我是一名探鬼主播呻顽,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼丹墨!你這毒婦竟也來了廊遍?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤贩挣,失蹤者是張志新(化名)和其女友劉穎昧碉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揽惹,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡被饿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了搪搏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狭握。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡钦铁,死狀恐怖捕透,靈堂內(nèi)的尸體忽然破棺而出谴忧,到底是詐尸還是另有隱情镜雨,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布恃疯,位于F島的核電站漏设,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏今妄。R本人自食惡果不足惜郑口,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盾鳞。 院中可真熱鬧犬性,春花似錦、人聲如沸腾仅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽推励。三九已至鹤耍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間验辞,已是汗流浹背稿黄。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留受神,地道東北人抛猖。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像鼻听,于是被迫代替她去往敵國和親财著。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容