1. 程式人生 > >仿支付寶笑臉重新整理載入動畫的實現

仿支付寶笑臉重新整理載入動畫的實現

看到支付寶的下拉重新整理有一個笑臉的動畫,因此自己也動手實現一下。效果圖如下:
這裡寫圖片描述

一、總體思路

1、靜態部分的笑臉。

這一部分的笑臉就是一個半圓弧,加上兩顆眼睛,這部分比較簡單,用於一開始的展示。

2、動態笑臉的實現。

2.1、先是從底部有一個圓形在運動,運動在左眼位置時把左眼給繪製,同時圓形繼續運動,運動到右眼位置時繪製右眼,圓形繼續運動到最右邊的位置。
2.2、當上面的圓形運動到最右邊時候,開始不斷繪製臉,從右向左,臉不斷增長,這裡臉設定為接近半個圓形的大小。
2.3、當臉畫完的時候,開始讓臉旋轉起來,就是一邊在增長的同時,另一邊是在縮短的。
2.4、最後臉的部分是慢慢縮為一個點的,此時動畫結束。
2.5、時間可以看做時最底部的那個圓形運動了兩週,因此可以用分數來表示每一部分的運動,如從底部開始到左眼睛的位置,用時比例為(1/4+1/8),最終控制每一部分的動畫比例的和加起來為2即可。
大概是這樣的時間比例:(1/4+1/8) + (1/4) +(1/8) +(1/2) +(1/4) +(1/4+1/4) ,其中1/4代表1/4個圓弧,也是1/4的時間,其它的類似。

二、程式碼實現

1、重寫onMeasure()方法

處理為wrap_content情況,那麼它的specMode是AT_MOST模式,在這種模式下它的寬/高等於spectSize,這種情況下view的spectSize是parentSize,而parentSize是父容器目前可以使用大小,就是父容器當前剩餘的空間大小, 就相當於使用match_parent一樣 的效果,因此我們可以設定一個預設的值。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super
.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpectMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpectSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpectMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpectSize = MeasureSpec.getSize(heightMeasureSpec); if
(widthSpectMode == MeasureSpec.AT_MOST && heightSpectMode == MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, mHeight); } else if (widthSpectMode == MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, heightSpectSize); } else if (heightSpectMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpectSize, mHeight); } }

2、在建構函式中呼叫init()方法

進行初始化,之所以看到運動中圓弧能夠在右邊增長的同時,左邊的也在減少是使用PathMeasure類中的getSegment方法來擷取任意一段長度的路徑。

private void init(Context context, AttributeSet attrs) {
        drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
                | Paint.FILTER_BITMAP_FLAG);
        lineWidth = dip2px(context, lineWidth);
        radius = dip2px(context, radius);
        path = new Path();
        pathCircle = new Path();
        pathCircle2 = new Path();
        //在path中新增一個順時針的圓,這時候路徑的起點和終點在最後邊
        //在畫前半部分的臉和運動中的臉,起點在最右邊比較方便的計算,但在最後那部分,運動的終點
        //是在圓形的底部,這樣把路徑圓進行轉換到底部,方便計算
        pathCircle.addCircle(0, 0, radius, Direction.CW);
        pathCircle2.addCircle(0, 0, radius, Direction.CW);
        //利用Matrix,讓pathCircle中的圓旋轉90度,這樣它的路徑的起點和終點都在底部了
        Matrix m = new Matrix();
        m.postRotate(90);
        pathCircle.transform(m);
        //畫臉的筆
        paint = new Paint();
        //畫眼睛的筆
        eyePaint = new Paint();
        paint.setColor(blue);
        eyePaint.setColor(blue);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(lineWidth);
        eyePaint.setStrokeWidth(lineWidth);
        //設定畫臉的筆的端點為圓角(即起點和終點都是圓角)
        paint.setStrokeCap(Paint.Cap.ROUND);
        //使用PathMeasure計算路徑的資訊
        pm = new PathMeasure();
        pm.setPath(pathCircle, false);
        pm2 = new PathMeasure();
        pm2.setPath(pathCircle2, false);
        //路徑的長度,兩個路徑都是圓形,因此只計算其中一個即可
        length = pm.getLength();
        eyeRadius = (float)(lineWidth/2.0+lineWidth/5.0);
    }

3、畫靜態笑臉

pm2.getSegment()方法可以獲取指定長度的路徑,然後儲存在path中,在第二步已經把一個圓加到path中去,並初始化了pm2了。

/**靜態的笑臉
     * @param canvas
     */
    private void first(Canvas canvas) {
        pm2.getSegment(10, length / 2-10, path, true);
        canvas.drawPath(path, paint);
        path = new Path();
        drawEye(canvas);
    }
    /**一開始畫的眼睛
     * @param canvas
     */
    public void drawEye(Canvas canvas) {
        float x = (float) ((radius) * Math.cos(Math.PI * 45 / 180));
        float y = x;
        canvas.drawCircle(-x, -y, eyeRadius , eyePaint);
        canvas.drawCircle(x, -y, eyeRadius , eyePaint);
    }

4、實現運動的圓形的方法,即動畫開始部分。

這裡記得要進行角度轉換,π=180

/**從底部開始畫一個在運動的圓,運動時間為0-3/4
     * 即從270度開始,逆時針到0度
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        float degree = 270 - 270 * 4 / 3 * fraction;
        float x = (float) ((radius ) * Math.cos(Math.PI * degree/180));
        float y = -(float) ((radius ) * Math.sin(Math.PI * degree/ 180));
        canvas.drawCircle(x, y, eyeRadius, eyePaint);
    }

5、在圓形運動的同時畫眼睛

在圓形運動到左眼的位置時,同時繪製左眼,時間為1/4+1/8=3/8
運動到右眼位置時繪製右眼,時間為1/4+1/8+1/4=5/8
兩個眼睛的位置都設為45度

/* @param canvas
     * @param pos 0代表畫左眼,1代表畫右眼
     */
public void drawOneEye(Canvas canvas, int pos) {
        float x = (float) ((radius) * Math.cos(Math.PI * 45 / 180));
        float y = x;
        if (pos == 0) {
            canvas.drawCircle(-x, -y, eyeRadius, eyePaint);
        }else if(pos==1){
            canvas.drawCircle(x, -y, eyeRadius , eyePaint);
        }
    }

6、動畫進行之後繪製笑臉

笑臉部分是半個圓,因此擷取的最大長度是length/2
用的時間是1/2,畫完之後fraction應該到了5/4的時間了,1/4+1/8+1/4+1/8+1/2=5/4

public void drawFace(Canvas canvas){
        //需要重新給path賦值
        path=null;
        path = new Path();
        //根據時間來擷取一定長度的路徑,儲存到path中,取值範圍(0,length/2)
        pm2.getSegment(0, (float) (length*(fraction-0.75)), path, true);
        canvas.drawPath(path, paint);
    }

7、笑臉繪製完成後,把它動起來。

把圓臉部分逆時針旋轉起來,擷取最大長度還是length/2, 用的是這個方法pm2.getSegment(),運動的時間為1/4時間,需要不斷改變起點和終點,這樣圓臉才會動起來

public void rotateFace(Canvas canvas){
        path=null;
        path = new Path();
        pm2.getSegment((float)(length*(fraction-5.0/4)), (float)(length*(fraction-5.0/4)+length*0.5), path, true);
        canvas.drawPath(path, paint);
    }

8、最後那部分動畫的實現。

剩下的1/4時間,就用另外一個PathMeasure,這個圓的路徑起點是底部的,在初始化時候已經進行轉換,因為這樣設定比較方便的計算它的終點位置。

public void drawLastFact(Canvas canvas){
        path = null;
        path = new Path();
        //從起點的1/4長度開始(即最左邊的圓點),到圓的路徑的終點(即底部)
        pm.getSegment((float)(1.0/4*length+3.0/2*(fraction-3.0/2)*length), (float)(length/2.0+length/8.0+(fraction-3.0/2)*length), path, true);
        canvas.drawPath(path, paint);
    }

9、屬性動畫的實現

public void performAnim() {
        //上面計算的時間比例,加起來就是2,是運動了兩週,因此這裡設定為(0,2)
        val = ValueAnimator.ofFloat(0, 2);
        val.setDuration(duration);
        val.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator arg0) {
                fraction = (float) arg0.getAnimatedValue();
                postInvalidate();
            }
        });
        val.setRepeatCount(repeaCount);
        val.start();
        val.setRepeatMode(ValueAnimator.RESTART);
    }

10 、在onDraw()方法中呼叫一上方法。

這裡的fraction的範圍是[0,2],每個片段就用分數表示,最後它們的和剛好是2。

@Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            if (changed) {
                mWidth = right - left;
                mHeight = bottom - top;
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //從畫布上去除鋸齒
        canvas.setDrawFilter(drawFilter);
        canvas.translate(mWidth / 2, mHeight / 2);
        if (fraction == -1||!val.isRunning())
            first(canvas);
        //從底部開始畫一個在運動的圓,運動時間為0-3/4
        if (0 < fraction && fraction < 0.75) {
            drawCircle(canvas);
        }
        //畫左眼
        if (fraction > 1.0 * 3 / 8&&fraction<1.0*6/4) {
            drawOneEye(canvas,0);
        }
        //畫右眼
        if(fraction>1.0*5/8&&fraction<1.0*6/4){
            drawOneEye(canvas, 1);
        }
        //畫臉
        if(fraction>=0.75&&fraction<=5.0/4){
            drawFace(canvas);
        }
        //把臉運動起來
        if(fraction>=5.0/4&&fraction<=(5.0/4+1.0/4)){
            rotateFace(canvas);
        }
        if(fraction>=6.0/4){
            drawLastFact(canvas);
        }
    }

11、其它的方法和欄位的定義

/**
     * 根據手機的解析度從 dp 的單位 轉成為 px(畫素)
     */
    public int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
    //欄位
    private final int blue = 0xff4aadff;
    private int mWidth = 200;
    private int mHeight = 200;
    private int radius = 20;
    private int lineWidth = 5;
    private float eyeRadius;
    Paint paint,eyePaint;
    DrawFilter drawFilter;
    Path path, pathCircle,pathCircle2;
    PathMeasure pm,pm2;
    float length;// 圓周長
    float fraction = -1;
    long duration = 2000;
    int repeaCount = 8;
    float x = 0, y = 0;
    ValueAnimator val;

11、自定義控制元件的使用

//在佈局中的設定
<com.example.test22.view.SmileView 
        android:id="@+id/smile"
        android:layout_width="match_parent"
        android:layout_height="100dp"/>

在Activity中,

SmileView smile;
smile = (SmileView)findViewById(R.id.smile);
//設定動畫執行時間,重複的次數。
smile.setDuration(2000);
smile.setRepeaCount(8);
//執行動畫
smile.performAnim();
//停止動畫
smile.cancelAnim();

三、總結

我覺得難點在於運動中圓弧的一邊增長的同時,另一邊在縮短的控制,計算的不好就很容易出現從一個片段到另外一個片段時候跳躍十分明顯,在這裡我用到兩個路徑的圓,一個圓的起點在最右邊,一個圓起點在底部,這樣在處理最後那部分,片段的終點需要回到底部時候比較方便。重點在於PathMeasure類的getSegment()方法的運用,同時會改變預設路徑的起點。

相關推薦

仿支付笑臉重新整理載入動畫實現

看到支付寶的下拉重新整理有一個笑臉的動畫,因此自己也動手實現一下。效果圖如下: 一、總體思路 1、靜態部分的笑臉。 這一部分的笑臉就是一個半圓弧,加上兩顆眼睛,這部分比較簡單,用於一開始的展示。 2、動態笑臉的實現。 2.1、先是從底部

android仿支付螞蟻森林載入動畫效果

一圖勝千言 偷過別人能量的小夥伴都熟悉這個載入效果,下面就講解一下實現過程。 1,自定義view 2,這裡要用到螞蟻森林的圖示,如圖 通過canvas.drawBitmap()畫出圖片。 3,通過PorterDuff.Mode.SRC_IN,給圖片填充想要的

一步一步實現仿支付金額圓環圖

現在應用裡面整合圖表是很常見的事了,簡單、直觀能給使用者更直觀的感受,包括支付寶,招商銀行App等,雖然有很多第三方的圖示庫,但是自己實現一個是不是很有成就感?主要優勢還是體現在自己可以實現一些特殊需求以及自己實現程式碼量小,不到150行。現在我們就來一步一步實現一個帶動畫的圓環圖,先放效果圖。 初始化一

Android_仿支付賬單列表(頭部停留及分頁資料載入)

       沒有辦法,米公設計的一個UI是stickyheaderlist(頭部停留)和分頁載入資料功能的整合,筆者原以為是米工自己拍著腦袋想出來的,還想進一步討論一下,後來才發現支付寶也是這麼做的,那好吧,做唄。 先上Demo完成效果圖(有點簡陋,但是這樣程式碼卻也更清

Android控制元件GridView之仿支付錢包首頁帶有分割線的GridView九宮格的完美實現

部落格時間:2015-02-04 15:03 今天我們來模仿一下支付寶錢包首頁中帶有分割線的GridView,俗稱九宮格。先上圖,是你想要的效果麼?如果是請繼續往下看。                                               

仿支付錢包首頁帶有分割線的GridView九宮格的完美實現

我們來模仿一下支付寶錢包首頁中帶有分割線的GridView,俗稱九宮格。先上圖,是你想要的效果麼?如果是請繼續往下看。 我們都知道ListView設定分割線是非常容易的,設定ListView的分割線顏色和寬度,只需要在佈局中定義android:divide

Swift3.0二維碼掃描實現(寫一個仿支付二維碼掃描的效果)

關鍵程式碼 import AVFoundation //獲取攝像裝置 let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) do { //建立輸入,輸出流

仿支付支付成功打勾動畫(關於PathMeasure你該知道的東西)

Android路徑動畫 沿預定路徑生成動畫影象(無論是直線或曲線)是做動畫時一種常見的情況。傳統方法是尋找Path的函式(貝塞爾函式用的比較多),帶入x的值來計算y的值, 然後使用座標(x,y)來完成動畫。 現在還有一種更簡單的方法:通過使用path和Pat

Android簡單實現仿支付新年紅包活動頁面的動態佈局效果

大家好,隔了很長一段時間沒有更新部落格了,有幾方面的原因,一是因為年底了在準備換工作的事情,二是因為年底了公司專案需要一個歸檔和總結的內容,所以做了一個月的開發共通元件的抽取成SDK的工作,內容都是一些平時比較常用的功能模組,比如像登入註冊,第三方註冊和分享

Android學習筆記-自定義仿支付ProgressBar動畫

最近開始學習自定義控制元件,看到支付寶支付的ProgressBar動畫感覺不錯,就學著也做一個這樣的ProgressBar。 首先看效果圖 原理:  一個執行緒無限改變進度畫弧形,當外部告知結束,通過判斷結果呈現成功或者失敗動畫,通過path座標緩慢變化就可以實現動畫效

仿支付/微信的password輸入框效果GridPasswordView解析

arp 主類 center 大小 str .get fcm android def 仿支付寶/微信的password輸入框效果GridPasswordView解析,把一些設置和一些關鍵的地方列了出來,方便大家使用,可能能夠省一部分的時間,也算是自己的積累吧。

aNDROID仿支付餅圖效果

餅圖 aid hao123 .com andro smart and lis oid sMaRT%E6%BC%82%E4%BA%AE%E6%97%B6%E9%92%9F%E2%80%94%E2%80%94%E6%BA%90%E4%BB%A3%E7%A0%81 http:/

仿支付首頁頭部伸縮效果

nat load err 默認 hub pen tle 滑動 bili 原文鏈接:https://mp.weixin.qq.com/s/GegMt7GDBCFVoUgFQWG3Sw 每次打開支付寶首頁滑動,頭部的伸縮動畫甚是吸引人。於是自己決定動手來實現一個。 無圖言虛

iOS仿支付首頁效果

tor www self get mage rsh .data 一定的 完全 代碼地址如下:http://www.demodashi.com/demo/12776.html 首先看一下效果 狀態欄紅色是因為使用手機錄屏的原因。 1.問題分析 1.導航欄A有兩組控件,

Android基礎控件——CardView的使用、仿支付銀行卡

內容 https prev use 設置 back com 支付 技術 今天有空學習了下CardView的使用,既然是使用,不凡使用一個實例操作一下 CardView是Android5.0的新控件,所以我們需要在dependencies中添加支持: CardVie

webApp 使用mui 仿支付主頁面案例

效果:         原始碼: <!doctype html> <html>     <head>        &

android仿支付輸入車牌號

這個是iOS的效果圖,差異不大,樓主主攻OC,見諒   需要用到的xml檔案 需要用到的類 number_or_letters.xml   <?xml version="1.0" encoding="UTF-8"?> <Keyboard an

css仿支付提現到賬過程時間軸

 本來想找一個現成的仿支付寶提現的時間軸,才發現沒有,我覺得不應該啊,又找了一會還是沒喲,算了,自己寫一個吧,分享出來,有需要的朋友可以直接拿走。 需要:1、iconfont(阿里巴巴向量圖示庫找個圖示,我已經找下了,你不想用也可以自己再找)   &nb

Android仿支付密碼輸入框(自定義數字鍵盤)

1.概述          Android自定義密碼輸入框,通過自定義輸入顯示框和自定義輸入鍵盤,實現仿支付寶數字鍵盤等。程式碼已託管到github,有需要的話可以去我的github下載。 可以自定義關閉圖示、文字內容、顏色、大小,彈框樣

react native 仿支付更多

公司要做一個支付寶更多的滑動效果。查詢react naive 沒有好的元件來實現。自己做了一個demo.   效果圖   程式碼實現: import React, {Component} from 'react'; import { StyleS