Android實現(xiàn)旋轉(zhuǎn)動畫的兩種方式案例詳解
練習案例
效果展示
前期準備
第一步:準備好顏色數(shù)組 res => values => colors.xml
<color name="orange">#FF9600</color> <color name="aqua">#02D1AC</color> <color name="yellow">#FFD200</color> <color name="bule">#00C6FF</color> <color name="green">#00E099</color> <color name="pink">#FF3891</color> <array name="splash_circle_colors"> <item>@color/orange</item> <item>@color/aqua</item> <item>@color/yellow</item> <item>@color/bule</item> <item>@color/green</item> <item>@color/pink</item> </array>
自定義 View java代碼編寫
方法一
關鍵思想: 屬性動畫 + 計算圓心
package com.wust.mydialog; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; /** * ClassName: com.wust.mydialog.MyRotateView <br/> * Description: <br/> * date: 2021/8/7 12:13<br/> * * @author yiqi<br /> * @QQ 1820762465 * @微信 yiqiideallife * @技術交流QQ群 928023749 */ public class MyRotateView extends View { //設置旋轉(zhuǎn)間隔時間 private int SPLASH_CIRCLE_ROTATE_TIME = 3000; //設置中心圓半徑 private float CENTER_CIRCLE_RADIUS; private float SMALL_CIRCLE_RADIUS; private float mCurrentSingle = 0f; private int[] mColorArray; private Paint mCirclePaint; private ValueAnimator va; public MyRotateView(Context context) { super(context); } public MyRotateView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public MyRotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); //初始化參數(shù) initParams(width,height); setMeasuredDimension(width,height); } private void initParams(int w, int h) { //設置中心圓半徑 CENTER_CIRCLE_RADIUS = 1/4.0f * w; //設置小圓的半徑 SMALL_CIRCLE_RADIUS = 1/25.0f * w; //獲取小球顏色 mColorArray = getResources().getIntArray(R.array.splash_circle_colors); //初始化畫筆 mCirclePaint = new Paint(); mCirclePaint.setDither(true); mCirclePaint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //繪制圓 drawSplashCircle(canvas); } private void drawSplashCircle(Canvas canvas) { //設置屬性動畫,讓小圓轉(zhuǎn)起來 //這里得注意,是個坑,你如果不判斷那球就不會動 因為會陷入死循環(huán) 值動畫將值設置為0 -> invalidate()重繪 -> 執(zhí)行draw 又將值設為0 if (va == null){ va = ObjectAnimator.ofFloat(0f, 2 * (float) Math.PI); va.setDuration(SPLASH_CIRCLE_ROTATE_TIME); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurrentSingle = (float) animation.getAnimatedValue(); // System.out.println("mCurrentSingle ->" + mCurrentSingle); invalidate(); } }); va.setInterpolator(new LinearInterpolator()); va.start(); } //計算每個小球的間隔 double spaceAngle = Math.PI*2/mColorArray.length; for (int i = 0; i < mColorArray.length; i++) { //為 每個球 畫筆 設置顏色 mCirclePaint.setColor(mColorArray[i]); //利用 勾股定理 計算 小圓 中心點 float cx = getWidth()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.cos(spaceAngle*i+mCurrentSingle)); float cy = getHeight()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.sin(spaceAngle*i+mCurrentSingle)); canvas.drawCircle(cx,cy,SMALL_CIRCLE_RADIUS,mCirclePaint); } } }
方法二
關鍵思想:旋轉(zhuǎn)畫布法
package com.wust.mydialog; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; /** * ClassName: com.wust.mydialog.MyRotateView <br/> * Description: <br/> * date: 2021/8/7 12:13<br/> * * @author yiqi<br /> * @QQ 1820762465 * @微信 yiqiideallife * @技術交流QQ群 928023749 */ public class MyRotateView extends View { //設置旋轉(zhuǎn)間隔時間 private int SPLASH_CIRCLE_ROTATE_TIME = 3000; //設置中心圓半徑 private float CENTER_CIRCLE_RADIUS; private float SMALL_CIRCLE_RADIUS; private float mCurrentSingle = 0f; private int[] mColorArray; private Paint mCirclePaint; private ValueAnimator va; public MyRotateView(Context context) { super(context); } public MyRotateView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public MyRotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); //初始化參數(shù) initParams(width,height); setMeasuredDimension(width,height); } private void initParams(int w, int h) { //設置中心圓半徑 CENTER_CIRCLE_RADIUS = 1/4.0f * w; //設置小圓的半徑 SMALL_CIRCLE_RADIUS = 1/25.0f * w; //獲取小球顏色 mColorArray = getResources().getIntArray(R.array.splash_circle_colors); //初始化畫筆 mCirclePaint = new Paint(); mCirclePaint.setDither(true); mCirclePaint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //繪制圓 drawSplashCircle(canvas); } private void drawSplashCircle(Canvas canvas) { //設置屬性動畫,讓小圓轉(zhuǎn)起來 //這里得注意,是個坑,你如果不判斷那球就不會動 因為會陷入死循環(huán) 值動畫將值設置為0 -> invalidate()重繪 -> 執(zhí)行draw 又將值設為0 if (va == null){ // va = ObjectAnimator.ofFloat(0f, 2 * (float) Math.PI); va = ObjectAnimator.ofFloat(0f, 360.0f); va.setDuration(SPLASH_CIRCLE_ROTATE_TIME); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurrentSingle = (float) animation.getAnimatedValue(); // System.out.println("mCurrentSingle ->" + mCurrentSingle); invalidate(); } }); va.setInterpolator(new LinearInterpolator()); va.start(); } //計算每個小球的間隔 // double spaceAngle = Math.PI*2/mColorArray.length; double spaceAngle = 360.0d/mColorArray.length; System.out.println("spaceAngle -> " + spaceAngle); //利用旋轉(zhuǎn)畫布法 canvas.save(); canvas.rotate(mCurrentSingle,getWidth()/2,getHeight()/2); for (int i = 0; i < mColorArray.length; i++) { canvas.rotate((float) spaceAngle,getWidth()/2,getHeight()/2); //為 每個球 畫筆 設置顏色 mCirclePaint.setColor(mColorArray[i]); //利用 勾股定理 計算 小圓 中心點 //float cx = getWidth()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.cos(spaceAngle*i+mCurrentSingle)); //float cy = getHeight()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.sin(spaceAngle*i+mCurrentSingle)); //利用旋轉(zhuǎn)畫布法 float cx = getWidth()/2 + CENTER_CIRCLE_RADIUS; float cy = getHeight()/2; canvas.drawCircle(cx,cy,SMALL_CIRCLE_RADIUS,mCirclePaint); } canvas.restore(); } }
易錯點總結:
1、canvas.rotate(mCurrentSingle,getWidth()/2,getHeight()/2);中 第一個參數(shù)傳的是角度(360度的那種),而 Math.cos();中 參數(shù)傳的是一個弧度(2π的那種)
2、canvas.rotate() 函數(shù)執(zhí)行之后對后續(xù)畫布上的操作都是有影響的,所以,得配合 canvas.save();和 canvas.restore();使用。因此,里面的canvas.rotate((float) spaceAngle,getWidth()/2,getHeight()/2);中spaceAngle不能乘 i 。
3、畫布的旋轉(zhuǎn)除了 canvas.rotate() 函數(shù) 可以實現(xiàn)外,還可以利用矩陣。代碼如下:
//創(chuàng)建矩陣 private Matrix mSpaceMatrix; //初始化旋轉(zhuǎn)矩陣 mSpaceMatrix = new Matrix(); //初始化旋轉(zhuǎn)矩陣 mSpaceMatrix.reset(); mSpaceMatrix.postRotate((float) spaceAngle,getWidth()/2,getHeight()/2); //畫布旋轉(zhuǎn)角度 canvas.concat(mSpaceMatrix);
完整代碼
package com.wust.mydialog; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; /** * ClassName: com.wust.mydialog.MyRotateView <br/> * Description: <br/> * date: 2021/8/7 12:13<br/> * * @author yiqi<br /> * @QQ 1820762465 * @微信 yiqiideallife * @技術交流QQ群 928023749 */ public class MyRotateView extends View { //設置旋轉(zhuǎn)間隔時間 private int SPLASH_CIRCLE_ROTATE_TIME = 3000; //設置中心圓半徑 private float CENTER_CIRCLE_RADIUS; private float SMALL_CIRCLE_RADIUS; private float mCurrentSingle = 0f; private int[] mColorArray; private Paint mCirclePaint; private ValueAnimator va; private Matrix mSpaceMatrix; public MyRotateView(Context context) { super(context); } public MyRotateView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public MyRotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); //初始化參數(shù) initParams(width,height); setMeasuredDimension(width,height); } private void initParams(int w, int h) { //設置中心圓半徑 CENTER_CIRCLE_RADIUS = 1/4.0f * w; //設置小圓的半徑 SMALL_CIRCLE_RADIUS = 1/25.0f * w; //獲取小球顏色 mColorArray = getResources().getIntArray(R.array.splash_circle_colors); //初始化畫筆 mCirclePaint = new Paint(); mCirclePaint.setDither(true); mCirclePaint.setAntiAlias(true); //初始化旋轉(zhuǎn)矩陣 mSpaceMatrix = new Matrix(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //繪制圓 drawSplashCircle(canvas); } private void drawSplashCircle(Canvas canvas) { //設置屬性動畫,讓小圓轉(zhuǎn)起來 //這里得注意,是個坑,你如果不判斷那球就不會動 因為會陷入死循環(huán) 值動畫將值設置為0 -> invalidate()重繪 -> 執(zhí)行draw 又將值設為0 if (va == null){ // va = ObjectAnimator.ofFloat(0f, 2 * (float) Math.PI); va = ObjectAnimator.ofFloat(0f, 360.0f); va.setDuration(SPLASH_CIRCLE_ROTATE_TIME); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurrentSingle = (float) animation.getAnimatedValue(); // System.out.println("mCurrentSingle ->" + mCurrentSingle); invalidate(); } }); va.setInterpolator(new LinearInterpolator()); va.start(); } //計算每個小球的間隔 // double spaceAngle = Math.PI*2/mColorArray.length; double spaceAngle = 360.0d/mColorArray.length; //初始化旋轉(zhuǎn)矩陣 mSpaceMatrix.reset(); mSpaceMatrix.postRotate((float) spaceAngle,getWidth()/2,getHeight()/2); //利用旋轉(zhuǎn)畫布法 canvas.save(); canvas.rotate(mCurrentSingle,getWidth()/2,getHeight()/2); for (int i = 0; i < mColorArray.length; i++) { // canvas.rotate((float) spaceAngle,getWidth()/2,getHeight()/2); // System.out.println("spaceAngle -> " + spaceAngle); canvas.concat(mSpaceMatrix); //為 每個球 畫筆 設置顏色 mCirclePaint.setColor(mColorArray[i]); //利用 勾股定理 計算 小圓 中心點 //float cx = getWidth()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.cos(spaceAngle*i+mCurrentSingle)); //float cy = getHeight()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.sin(spaceAngle*i+mCurrentSingle)); //利用旋轉(zhuǎn)畫布法 float cx = getWidth()/2 + CENTER_CIRCLE_RADIUS; float cy = getHeight()/2; canvas.drawCircle(cx,cy,SMALL_CIRCLE_RADIUS,mCirclePaint); } canvas.restore(); } }
注意事項:
1、canvas.concat(mSpaceMatrix);對畫布的操作也會對后面進行影響
2、Android中Matrix的set、pre、post的區(qū)別
說set、pre、post的區(qū)別之前,先說說Matrix。
Matrix包含一個3 X 3的矩陣,專門用于圖像變換匹配。
Matrix提供了四種操作:
- translate(平移)
- rotate(旋轉(zhuǎn))
- scale(縮放)
- skew(傾斜)
也就是說這4種操作都是對這個3 X 3的矩陣設值來達到變換的效果。
Matrix沒有結構體,它必須被初始化,通過reset或set方法。
OK,Matrix介紹完了,我們來看看set、pre、post的區(qū)別。
pre是在隊列最前面插入,post是在隊列最后面追加,而set先清空隊列在添加(這也是上文提到的“Matrix沒有結構體,它必須被初始化,通過reset或set方法”的原因)。
下面通過一些例子具體說明:
- matrix.preScale(2f,1f);
- matrix.preTranslate(5f, 0f);
- matrix.postScale(0.2f, 1f);
- matrix.postTranslate(0.5f, 0f);
執(zhí)行順序:translate(5, 0) -> scale(2f, 1f) -> scale(0.2f, 1f) -> translate(0.5f, 0f)
- matrix.postTranslate(2f, 0f);
- matrix.preScale(0.2f, 1f);
- matrix.setScale(1f, 1f);
- matrix.postScale(5f, 1f);
- matrix.preTranslate(0.5f, 0f);
執(zhí)行順序:translate(0.5f, 0f) -> scale(1f, 1f) -> scale(5f, 1)
到此這篇關于Android實現(xiàn)旋轉(zhuǎn)動畫的兩種方式的文章就介紹到這了,更多相關android旋轉(zhuǎn)動畫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android開發(fā)使用json實現(xiàn)服務器與客戶端數(shù)據(jù)的交互功能示例
這篇文章主要介紹了Android開發(fā)使用json實現(xiàn)服務器與客戶端數(shù)據(jù)的交互功能,結合具體實例形式分析了Android使用json格式數(shù)據(jù)在服務器與客戶端傳遞實現(xiàn)數(shù)據(jù)庫查詢功能的步驟與相關操作技巧,需要的朋友可以參考下2017-10-10Android使用TextInputLayout創(chuàng)建登陸頁面
這篇文章主要為大家詳細介紹了Android使用TextInputLayout創(chuàng)建登陸頁面,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10Android實現(xiàn)短信加密功能(發(fā)送加密短信、解密本地短信)
這篇文章主要介紹了android實現(xiàn)短信加密功能的相關資料,功能包括發(fā)送加密短信、解密本地短信,感興趣的小伙伴們可以參考一下2016-01-01Android自定義View實現(xiàn)兩種二維碼的掃描效果
這篇文章主要為大家詳細介紹了Android如何自定義View實現(xiàn)兩種二維碼的掃描效果,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-01-01Android?Jetpack庫剖析之ViewModel組件篇
這篇文章主要介紹了Android?Jetpack架構組件?ViewModel詳解,ViewModel類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)存在,ViewModel類旨在以注重生命周期的方式存儲和管理界面相關的數(shù)據(jù)。感興趣可以來學習一下2022-07-07Android Studio做超好玩的拼圖游戲 附送詳細注釋源碼
這篇文章主要介紹了用Android Studio做的一個超好玩的拼圖游戲,你是0基礎Android小白也能包你學會,另外附送超詳細注釋的源碼,建議收藏!2021-08-08Android基于ViewDragHelper仿QQ5.0側滑界面效果
這篇文章主要介紹了Android基于ViewDragHelper仿QQ5.0側滑界面效果,具有一定的,感興趣的小伙伴們可以參考一下2016-07-07