PathMeasuer實現一個仿支付完成動畫

前言

Android中的path是一個非常重要的一個類,我想大家應該都知道通過path可以畫出很多東西,但這只是僅限于能畫幾個簡單的東西,如果想要定位任意一個給定Path的任意一個點的坐標,或者通過Path實現一些炫酷的動畫,我們就必須要了解PathMeasure這個類,這個類真的是很強大的,可以做很多好看的矢量動畫,當然前提是你熟練了解并運用這個類,今天我們就來了解一下這個類吧。
嘿嘿,先來看看用PathMeasure實現的一個簡單的仿支付成功之后的一個動畫demo吧。

Demo展示

pathview

PathMeasure介紹

構造方法

  • PathMeasure()
  • PathMeasure(Path path, boolean forceClosed)
    這里主要說一下forceClosed參數,其他的見名知意了。為True的話,你這個關聯的path不管它的路徑是否是閉合的,最后執行完PathMeasure之后都會閉合這條path,為false的話就不會,一般都會設置成false,當然具體要看你實現什么樣的效果。

天干物燥,小心雷區,女施主小心點,男施主就算了,踩了就踩了吧

image.png
  • 不論 forceClosed是true 或者 false, 都不會影響原有Path的狀態,即 Path 與 PathMeasure 關聯之后,之前的的 Path 不會有任何改變。
  • forceClosed 的值會影響測量結果,如果 Path本身 未閉合,但在與 PathMeasure 關聯的時候設置 forceClosed 為 true 時,測量結果可能會比 Path 實際長度稍長一點,獲取到到是該 Path 閉合時的狀態。

常用方法

  • getLength
    獲取關聯的path長度
  • isClosed
    用于判斷 Path 是否閉合,forceClosed設置true的時候,這個值一定是true
  • getSegment
boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)

這是pathmeasure最重要的方法了,這個方法的意思,截取PathMeasure關聯的path中的任何一個線段。
怎么樣一條線段呢,在關聯的path中起點是startD,終點是stopD,然后截取的線段存在dst這個變量中,你可以new 一個path當作這個變量,然后拿來用。這里有個雷區要注意一下,記住是

  • 添加到dst不是替換dst
  • 添加到dst不是替換dst
  • 添加到dst不是替換dst
    重要的事情說三遍,就是說加入dst之前是有好幾條線段組成的話,通過這個方法添加的path會放在最后。
    最后說一下這個startWithMoveTo這個變量。這個變量關系著新添加的這條path的起始點坐標會不會變化,加入為false的話,就不會變化,該怎么樣還是怎么樣;加入為true的話,就有關系了,這個起始坐標變為dst原來添加的最后一個path的終點坐標,相當于就是前后兩條線連起來了,這樣說能理解吧。
    不能的話我舉個栗子。
image.png

我放兩張圖就知道了。

  • startWithMoveTo為false的時候
image.png
  • startWithMoveTo為true的時候
image.png

起點坐標由原來位置1變為位置2
就變成了這個樣字的線段

image.png

硬件加速的Bug

由于硬件加速的問題,PathMeasure中的getSegment在講Path添加到dst數組中時會被導致一些錯誤,這里有兩個方法可以試一下

  • 通過mDst.lineTo(0,0)來避免這樣一個Bug。
  • 通過配置來關閉硬件加速 傳送門,通常我們在canvas級別上關閉就可以了,代碼如下
 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (canvas.isHardwareAccelerated()) {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
        drawPayPath(canvas);
    }
  • nextContour
    path是可以有多條線段構成的,這個nextContour相當于就是遍歷PathMeasure中的關聯的path的所有線段。
    通過這個方法,可以很方便的實現我下面這個一起畫的功能。可以看的到勾號和圓形是一起畫的。

    示例代碼如下
//sucessPath添加兩條線段
 Path successPath = new Path();
 PathMeasure mPathMeasure = new PathMeasure ();
 successPath.addPath(circlePath);
 successPath.addPath(rightPath);

 mPathMeasure.setPath(path, false);
 while (mPathMeasure.nextContour()) {
    Path dsts = new Path();
    float starts = 0f;
    float ends = mPathMeasure.getLength() * animatorValue;
    mPathMeasure.getSegment(starts, ends, dsts, true);
    canvas.drawPath(dsts, mPaint);
  }
  • boolean getPosTan (float distance, float[] pos, float[] tan)
    1. distance是距離 Path 起點的長度,范圍是0 <= distance <= getLength
    2. 得到點的位置坐標和這個坐標點的正切值分別存到pos和tan中
      這個參數很有用,通常我們在path中放一個小東西上跑來跑去的時候,就用到這個了,通常是先通過tan獲取角度值,代碼如下
measure.getPosTan(measure.getLength() * currentValue, pos, tan);        // 獲取當前位置的坐標以及趨勢
mMatrix.reset();                                                        // 重置Matrix
float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); // 計算圖片旋轉角度
mMatrix.postRotate(degrees, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);   // 旋轉圖片
mMatrix.postTranslate(pos[0] - mBitmap.getWidth() / 2, pos[1] - mBitmap.getHeight() / 2);   // 將圖片繪制中心調整到與當前點重合

然后再畫上這個圖片就行了。實現的就跟下面差不多。


基礎知識差不多就說到這里了,下面說說這個demo

Demo講解

  • 自定義的屬性
<declare-styleable name="PayPathView">
        <attr name="stroke_width" format="dimension"></attr>
        <attr name="animator_duration" format="integer"></attr>
        <attr name="stroke_color" format="color"></attr>
        <attr name="paying_count" format="integer"/>
        <attr name="right_path_color" format="color"/>
        <attr name="error_path_color" format="color"/>
        <attr name="play_together" format="boolean"/>
        <attr name="auto_exit" format="boolean"></attr>
        <attr name="animator_type">
            <enum name="success_animator" value="0"/>
            <enum name="failure_animator" value="1"/>
        </attr>
</declare-styleable>

對應的意思在view代碼里頭有解釋

    private int payingCountNums;//需要執行支付動畫的總次數

    private int stroke_width;//畫筆的寬度

    private int stroke_color;//畫筆的顏色

    private int rightPathColor;//勾路徑的顏色

    private int errorPathColor;//叉號路徑的顏色

    private boolean isAutoExit;//是否播放退出動畫,默認為false

    private boolean isPlayingTogether;//動畫是否一起播放,默認為false

    private int animationDuration = 1000;//動畫時間,默認為一秒

    private int animatorType = -1;//動畫類型 0代表播放成功動畫,1代表播放失敗動畫

  • 五種狀態
/**
     * 支付狀態枚舉
     */
    public static enum payState {
        NONE,
        PAYING,
        SUCCESS,
        FAILURE,
        EXIT
    }

每個狀態對應一個動畫,通過狀態來播放動畫來繪畫不同的path,可以控制不同的狀態實現。自由的控制繪畫哪條path。

  • 結合了屬性動畫控制繪畫路徑
/**
     * 初始化所有動畫
     */
    private void initAnimation() {
        payingAnimation = ValueAnimator.ofFloat(0, 1).setDuration(animationDuration);
        successAnimation = ValueAnimator.ofFloat(0, 1).setDuration(animationDuration);
        failureAnimation = ValueAnimator.ofFloat(0, 1).setDuration(animationDuration);
        exitAnimation = ValueAnimator.ofFloat(1, 0).setDuration(animationDuration);

        payingAnimation.addListener(mAnimationListener);
        successAnimation.addListener(mAnimationListener);
        failureAnimation.addListener(mAnimationListener);
        exitAnimation.addListener(mAnimationListener);

        payingAnimation.addUpdateListener(mUpdateListener);
        successAnimation.addUpdateListener(mUpdateListener);
        failureAnimation.addUpdateListener(mUpdateListener);
        exitAnimation.addUpdateListener(mUpdateListener);
    }
/**
     * 初始化監聽器
     */
    private void initListener() {
        mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animatorValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        };
    }

可以看到我們是通過屬性動畫來控制繪畫路徑的長度的。取到相應比例值animatorValue,然后再invalidate一下,在onDraw方法計算要畫的path的長度,這樣就實現了動畫效果。具體看代碼吧,有注釋,都比較簡單。

  • 外部調用方法
<com.example.administrator.pathview.PayPathView
            android:id="@+id/serach"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_marginLeft="20dp"
            app:animator_duration="1000"
            app:animator_type="success_animator"
            app:error_path_color="@color/colorAccent"
            app:paying_count="2"
            app:play_together="false"
            app:right_path_color="@color/gray"
            app:stroke_color="@color/white"
            app:stroke_width="6dp"/>

外部代碼設置

   mPayPathView.setAnimatorType(0);//播放成功的動畫
   mPayPathView.setSuccessColor(selectedColor);   //成功路徑的顏色
   mPayPathView.setPlayingTogether(true);  //分段path是否一起播放                          
   mPayPathView.setAutoExit(true);//是否播放回退動畫
   mPayPathView.start();//開始動畫

結語

代碼就說到這里了,代碼說到這里了,里面都有詳細的解釋,思路還是比較簡單易懂的。如果掌握這個例子,相信你對PathMeasure就有一定的了解了。掌握基礎用法并不難,難的是要聯想并應用到實際場景當中,代碼的初衷不就是這樣嗎,這也是我們需要多想的要給方面,畢竟代碼離不開生活。
源代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容