Android仿支付寶笑臉?biāo)⑿录虞d動畫的實現(xiàn)代碼
看到支付寶的下拉刷新有一個笑臉的動畫,因此自己也動手實現(xiàn)一下。效果圖如下:
一、總體思路
1、靜態(tài)部分的笑臉。
這一部分的笑臉就是一個半圓弧,加上兩顆眼睛,這部分比較簡單,用于一開始的展示。
2、動態(tài)笑臉的實現(xiàn)。
2.1、先是從底部有一個圓形在運動,運動在左眼位置時把左眼給繪制,同時圓形繼續(xù)運動,運動到右眼位置時繪制右眼,圓形繼續(xù)運動到最右邊的位置。
2.2、當(dāng)上面的圓形運動到最右邊時候,開始不斷繪制臉,從右向左,臉不斷增長,這里臉設(shè)置為接近半個圓形的大小。
2.3、當(dāng)臉畫完的時候,開始讓臉旋轉(zhuǎn)起來,就是一邊在增長的同時,另一邊是在縮短的。
2.4、最后臉的部分是慢慢縮為一個點的,此時動畫結(jié)束。
2.5、時間可以看做時最底部的那個圓形運動了兩周,因此可以用分?jǐn)?shù)來表示每一部分的運動,如從底部開始到左眼睛的位置,用時比例為(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的時間,其它的類似。
二、代碼實現(xiàn)
1、重寫onMeasure()方法
處理為wrap_content情況,那么它的specMode是AT_MOST模式,在這種模式下它的寬/高等于spectSize,這種情況下view的spectSize是parentSize,而parentSize是父容器目前可以使用大小,就是父容器當(dāng)前剩余的空間大小, 就相當(dāng)于使用match_parent一樣 的效果,因此我們可以設(shè)置一個默認(rèn)的值。
@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、在構(gòu)造函數(shù)中調(diào)用init()方法
進(jìn)行初始化,之所以看到運動中圓弧能夠在右邊增長的同時,左邊的也在減少是使用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中添加一個順時針的圓,這時候路徑的起點和終點在最后邊 //在畫前半部分的臉和運動中的臉,起點在最右邊比較方便的計算,但在最后那部分,運動的終點 //是在圓形的底部,這樣把路徑圓進(jìn)行轉(zhuǎn)換到底部,方便計算 pathCircle.addCircle(0, 0, radius, Direction.CW); pathCircle2.addCircle(0, 0, radius, Direction.CW); //利用Matrix,讓pathCircle中的圓旋轉(zhuǎn)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); //設(shè)置畫臉的筆的端點為圓角(即起點和終點都是圓角) 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、畫靜態(tài)笑臉
pm2.getSegment()方法可以獲取指定長度的路徑,然后保存在path中,在第二步已經(jīng)把一個圓加到path中去,并初始化了pm2了。
/**靜態(tài)的笑臉 * @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、實現(xiàn)運動的圓形的方法,即動畫開始部分。
這里記得要進(jìn)行角度轉(zhuǎn)換,π=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
兩個眼睛的位置都設(shè)為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、動畫進(jìn)行之后繪制笑臉
笑臉部分是半個圓,因此截取的最大長度是length/2
用的時間是1/2,畫完之后fraction應(yīng)該到了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(); //根據(jù)時間來截取一定長度的路徑,保存到path中,取值范圍(0,length/2) pm2.getSegment(0, (float) (length*(fraction-0.75)), path, true); canvas.drawPath(path, paint); }
7、笑臉繪制完成后,把它動起來。
把圓臉部分逆時針旋轉(zhuǎn)起來,截取最大長度還是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、最后那部分動畫的實現(xiàn)。
剩下的1/4時間,就用另外一個PathMeasure,這個圓的路徑起點是底部的,在初始化時候已經(jīng)進(jìn)行轉(zhuǎn)換,因為這樣設(shè)置比較方便的計算它的終點位置。
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、屬性動畫的實現(xiàn)
public void performAnim() { //上面計算的時間比例,加起來就是2,是運動了兩周,因此這里設(shè)置為(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()方法中調(diào)用一上方法。
這里的fraction的范圍是[0,2],每個片段就用分?jǐn)?shù)表示,最后它們的和剛好是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、其它的方法和字段的定義
/** * 根據(jù)手機的分辨率從 dp 的單位 轉(zhuǎn)成為 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、自定義控件的使用
//在布局中的設(shè)置 <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); //設(shè)置動畫執(zhí)行時間,重復(fù)的次數(shù)。 smile.setDuration(2000); smile.setRepeaCount(8); //執(zhí)行動畫 smile.performAnim(); //停止動畫 smile.cancelAnim();
三、總結(jié)
我覺得難點在于運動中圓弧的一邊增長的同時,另一邊在縮短的控制,計算的不好就很容易出現(xiàn)從一個片段到另外一個片段時候跳躍十分明顯,在這里我用到兩個路徑的圓,一個圓的起點在最右邊,一個圓起點在底部,這樣在處理最后那部分,片段的終點需要回到底部時候比較方便。重點在于PathMeasure類的getSegment()方法的運用,同時會改變默認(rèn)路徑的起點。
以上所述是小編給大家介紹的Android仿支付寶笑臉?biāo)⑿录虞d動畫的實現(xiàn)代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- android實現(xiàn)加載動畫對話框
- Android 自定義加載動畫Dialog彈窗效果的示例代碼
- android自定義波浪加載動畫的實現(xiàn)代碼
- Android 使用 Path 實現(xiàn)搜索動態(tài)加載動畫效果
- Android自定義帶加載動畫效果的環(huán)狀進(jìn)度條
- Android自定義view利用Xfermode實現(xiàn)動態(tài)文字加載動畫
- Android帶數(shù)字或紅點的底部導(dǎo)航攔和聯(lián)網(wǎng)等待加載動畫示例
- Android Glide圖片加載(加載監(jiān)聽、加載動畫)
- Android實現(xiàn)跳動的小球加載動畫效果
- Android實現(xiàn)仿微軟系統(tǒng)加載動畫效果
相關(guān)文章
Android開發(fā)學(xué)習(xí)實現(xiàn)簡單計算器
這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)一個簡單計算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-04-04Android 中使用 dlib+opencv 實現(xiàn)動態(tài)人臉檢測功能
完成 Android 相機預(yù)覽功能以后,在此基礎(chǔ)上我使用 dlib 與 opencv 庫做了一個關(guān)于人臉檢測的 demo。接下來通過本文給大家介紹Android 中使用 dlib+opencv 實現(xiàn)動態(tài)人臉檢測功能 ,需要的朋友可以參考下2018-11-11Android Studio生成函數(shù)注釋的實現(xiàn)方法
這篇文章主要介紹了Android Studio生成函數(shù)注釋的實現(xiàn)方法的相關(guān)資料,希望通過本文大家能夠?qū)崿F(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09Android斷點續(xù)傳下載器JarvisDownloader的示例
本篇文章主要介紹了Android斷點續(xù)傳下載器JarvisDownloader的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05Android WebView使用方法詳解 附j(luò)s交互調(diào)用方法
這篇文章主要為大家詳細(xì)介紹了Android WebView使用方法詳解,文中附j(luò)s交互調(diào)用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05