您好,登錄后才能下訂單哦!
先上個效果圖,以免大家跑錯地了。
嗯,除了只能錄三秒,其他沒啥問題。
下面分析一下怎么實現(xiàn)上面這個效果。
理性分析后我們可以看到是幾個小球繞著一個圓進行運動,那這里面的重點我們看看什么。
繪制五個球,沒什么難度,讓球繞圓進行運動,這個好像我們沒有見到是怎么去實現(xiàn)了,那下就說這個。
從本質(zhì)上看,球繞圓運動,其實我們可以看作是一個物體繞指定的路勁運動,那我們就有下面幾個東西需要說一下:
1:Path
2:ValueAnimator
3:PathMeasure
前兩個大家應(yīng)該都見過,一個是路徑,就是可以自己繪制路線的一個工具,一個是動畫,用來指定物體運動的工具,那第三個是一個關(guān)于測量路徑的類。
下面說說PathMeasure的用法。
首先是初始化:
pathMeasure = new PathMeasure(path, false);
兩個參數(shù)第一個,第一個就是我們需要用到的路徑,第二個參數(shù)意思就是這個以路徑頭尾是否相連來計算結(jié)果,通常我們就寫false就行,不會有問題。
然后是用法:
private float[] mCurrentPositionOne = new float[2]; float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); 我們可以看見把一個二維數(shù)組放到了getPosTan這個方法里面,然后還有一個animation,這里的animation來自哪里呢?來自這里:valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); postInvalidate(); } });
看見沒,是動畫的監(jiān)聽里面來的,getPosTan的最后一個參數(shù)通常也就寫null就行了,那么這整個一行的代碼意思就是當(dāng)動畫發(fā)生了變化,就執(zhí)行這行代碼,然后這行代碼會把這個時間點的路徑上的坐標(biāo)賦值給mCurrentPositionOne。
那我們獲取到看這個路徑上的坐標(biāo)點怎么辦呢?
立馬用來ondraw里面啊,我的小球此時就可以根據(jù)這個坐標(biāo)點去繪制自己的位置,這個的話,當(dāng)動畫開始時,小球就會不斷接受新的坐標(biāo),然后不斷重繪,最終產(chǎn)生旋轉(zhuǎn)小球的效果。
我先把屬性動畫的代碼貼出來:
if (valueAnimatorOne == null) { valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorOne.setDuration(800); // 減速插值器 valueAnimatorOne.setInterpolator(new DecelerateInterpolator()); valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); postInvalidate(); } }); valueAnimatorOne.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateOne = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateOne = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorOne.start();
我寫了個800,也就是動畫的維持時間,但是我們發(fā)現(xiàn)有啊后幾個小球,所以我們需要繪制好幾個小球,然后給他們不同的動畫,為什么呢?因為動畫都一樣,小球就疊加在一起了,我們就只能看見一個球了。
說到這里的話,我們的目標(biāo)算時完成了,具體的操作,大家參考以下代碼,或者去:android自定義View索引
里面動畫的demo進行下載,大家隨意,下面給出代碼:
/** * 仿視頻加載動畫,旋轉(zhuǎn)的藍色小球 */ public class RotaryBall extends View { private Path rotationPath; private float radius; private Paint circlePaintOne; private PathMeasure pathMeasure; private int finishAnimateOne = 0; // 用來判斷當(dāng)前動畫有沒有開始 private int finishAnimateTwo = 0; // 用來判斷當(dāng)前動畫有沒有開始 private int finishAnimateThree = 0; // 用來判斷當(dāng)前動畫有沒有開始 private int finishAnimateFour = 0; // 用來判斷當(dāng)前動畫有沒有開始 private int finishAnimateFive = 0; // 用來判斷當(dāng)前動畫有沒有開始 private Handler handler; private float[] mCurrentPositionOne = new float[2]; private float[] mCurrentPositionTwo = new float[2]; private float[] mCurrentPositionThree = new float[2]; private float[] mCurrentPositionFour = new float[2]; private float[] mCurrentPositionFive = new float[2]; private ValueAnimator valueAnimatorOne = null; private ValueAnimator valueAnimatorTwo = null; private ValueAnimator valueAnimatorThree = null; private ValueAnimator valueAnimatorFour = null; private ValueAnimator valueAnimatorFive = null; private int currentStatus = -1; //-1表示第一次運行,0表示動畫結(jié)束或者沒開始,1表示正在運動中 private boolean animateOrNot = true; //用來決定是否開啟動畫 public RotaryBall(Context context) { super(context); initData(); } public RotaryBall(Context context, AttributeSet attrs) { super(context, attrs); initData(); } private void initData() { rotationPath = new Path(); circlePaintOne = new Paint(); circlePaintOne.setColor(Color.BLUE); circlePaintOne.setAntiAlias(true); handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 4: if (finishAnimateOne == 0) { startAnimatorOne(); } if (finishAnimateTwo == 0) { startAnimatorTwo(); } if (finishAnimateThree == 0) { startAnimatorThree(); } if (finishAnimateFour == 0) { startAnimatorFour(); } if (finishAnimateFive == 0) { startAnimatorFive(); } currentStatus = 0; } } }; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); radius = getMeasuredWidth() / 2; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // rotationPath.addCircle(radius, radius, radius - 10, CW); rotationPath.moveTo(radius, 0 + 10); rotationPath.cubicTo(radius, 0 + 10, radius * 2 - 10, 0 + 10, radius * 2 - 10, radius); rotationPath.cubicTo(radius * 2 - 10, radius, radius * 2 - 10, radius * 2 - 10, radius, radius * 2 - 10); rotationPath.cubicTo(radius, radius * 2 - 10, 0 + 10, radius * 2 - 10, 0 + 10, radius); rotationPath.cubicTo(0 + 10, radius, 0 + 10, 0 + 10, radius, 0 + 10); rotationPath.close(); pathMeasure = new PathMeasure(rotationPath, false); //下面繪制不同半徑的小圓 canvas.drawCircle(mCurrentPositionOne[0], mCurrentPositionOne[1], 10, circlePaintOne); canvas.drawCircle(mCurrentPositionTwo[0], mCurrentPositionTwo[1], 9, circlePaintOne); canvas.drawCircle(mCurrentPositionThree[0], mCurrentPositionThree[1], 7, circlePaintOne); canvas.drawCircle(mCurrentPositionFour[0], mCurrentPositionFour[1], 5, circlePaintOne); canvas.drawCircle(mCurrentPositionFive[0], mCurrentPositionFive[1], 3, circlePaintOne); if (currentStatus == -1) { Message message = new Message(); message.what = 4; handler.sendMessage(message); } if (animateOrNot) { if (currentStatus == 0) { currentStatus = 1; new Thread() { //用線程來統(tǒng)一五個圓的周期 @Override public void run() { super.run(); try { Log.d("thread", "thread"); Thread.sleep(1600); Message message = new Message(); message.what = 4; handler.sendMessage(message); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } } } //供外部調(diào)用,開始動畫 public void startAnimate() { if (!animateOrNot) { animateOrNot = true; currentStatus = -1; invalidate(); } } //供外部調(diào)用,停止動畫 public void stopAnimate() { if (animateOrNot) { animateOrNot = false; } } //界面被銷毀 @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); stopAnimate(); clearAllAnimation(); } //清除所有動畫效果 private void clearAllAnimation() { if (valueAnimatorOne != null){ if (valueAnimatorOne.isRunning()){ valueAnimatorOne.cancel(); } valueAnimatorOne.removeAllUpdateListeners(); valueAnimatorOne = null; } if (valueAnimatorTwo != null){ if (valueAnimatorTwo.isRunning()){ valueAnimatorTwo.cancel(); } valueAnimatorTwo.removeAllUpdateListeners(); valueAnimatorTwo = null; } if (valueAnimatorThree != null){ if (valueAnimatorThree.isRunning()){ valueAnimatorThree.cancel(); } valueAnimatorThree.removeAllUpdateListeners(); valueAnimatorThree = null; } if (valueAnimatorFour != null){ if (valueAnimatorFour.isRunning()){ valueAnimatorFour.cancel(); } valueAnimatorFour.removeAllUpdateListeners(); valueAnimatorFour = null; } if (valueAnimatorFive != null){ if (valueAnimatorFive.isRunning()){ valueAnimatorFive.cancel(); } valueAnimatorFive.removeAllUpdateListeners(); valueAnimatorFive = null; } } //開始第一個小球的動畫 private void startAnimatorOne() { if (valueAnimatorOne == null) { Log.d("valueAnimatorOne", "valueAnimatorOne"); valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorOne.setDuration(800); // 減速插值器 valueAnimatorOne.setInterpolator(new DecelerateInterpolator()); valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); postInvalidate(); } }); valueAnimatorOne.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateOne = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateOne = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorOne.start(); } //開始第二個小球的動畫 private void startAnimatorTwo() { if (valueAnimatorTwo == null) { valueAnimatorTwo = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorTwo.setDuration(1000); // 減速插值器 valueAnimatorTwo.setInterpolator(new DecelerateInterpolator()); valueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionTwo, null); postInvalidate(); } }); valueAnimatorTwo.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateTwo = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateTwo = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorTwo.start(); } //開始第三個小球的動畫 private void startAnimatorThree() { if (valueAnimatorThree == null) { valueAnimatorThree = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorThree.setDuration(1200); // 減速插值器 valueAnimatorThree.setInterpolator(new DecelerateInterpolator()); valueAnimatorThree.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionThree, null); postInvalidate(); } }); valueAnimatorThree.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateThree = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateThree = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorThree.start(); } //開始第四個小球的動畫 private void startAnimatorFour() { if (valueAnimatorFour == null) { valueAnimatorFour = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorFour.setDuration(1400); // 減速插值器 valueAnimatorFour.setInterpolator(new DecelerateInterpolator()); valueAnimatorFour.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionFour, null); postInvalidate(); } }); valueAnimatorFour.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateFour = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateFour = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorFour.start(); } //開始第五個小球的動畫 private void startAnimatorFive() { if (valueAnimatorFive == null) { valueAnimatorFive = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorFive.setDuration(1600); // 減速插值器 valueAnimatorFive.setInterpolator(new DecelerateInterpolator()); valueAnimatorFive.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionFive, null); postInvalidate(); } }); valueAnimatorFive.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateFive = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateFive = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorFive.start(); } }
總結(jié)
以上所述是小編給大家介紹的Android仿視頻加載旋轉(zhuǎn)小球動畫實例代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對億速云網(wǎng)站的支持!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。