Android仿視頻加載旋轉(zhuǎn)小球動(dòng)畫(huà)效果的實(shí)例代碼
先上個(gè)效果圖,以免大家跑錯(cuò)地了。

嗯,除了只能錄三秒,其他沒(méi)啥問(wèn)題。
下面分析一下怎么實(shí)現(xiàn)上面這個(gè)效果。
理性分析后我們可以看到是幾個(gè)小球繞著一個(gè)圓進(jìn)行運(yùn)動(dòng),那這里面的重點(diǎn)我們看看什么。
繪制五個(gè)球,沒(méi)什么難度,讓球繞圓進(jìn)行運(yùn)動(dòng),這個(gè)好像我們沒(méi)有見(jiàn)到是怎么去實(shí)現(xiàn)了,那下就說(shuō)這個(gè)。
從本質(zhì)上看,球繞圓運(yùn)動(dòng),其實(shí)我們可以看作是一個(gè)物體繞指定的路勁運(yùn)動(dòng),那我們就有下面幾個(gè)東西需要說(shuō)一下:
1:Path
2:ValueAnimator
3:PathMeasure
前兩個(gè)大家應(yīng)該都見(jiàn)過(guò),一個(gè)是路徑,就是可以自己繪制路線的一個(gè)工具,一個(gè)是動(dòng)畫(huà),用來(lái)指定物體運(yùn)動(dòng)的工具,那第三個(gè)是一個(gè)關(guān)于測(cè)量路徑的類(lèi)。
下面說(shuō)說(shuō)PathMeasure的用法。
首先是初始化:
pathMeasure = new PathMeasure(path, false);
兩個(gè)參數(shù)第一個(gè),第一個(gè)就是我們需要用到的路徑,第二個(gè)參數(shù)意思就是這個(gè)以路徑頭尾是否相連來(lái)計(jì)算結(jié)果,通常我們就寫(xiě)false就行,不會(huì)有問(wèn)題。
然后是用法:
private float[] mCurrentPositionOne = new float[2];
float value = (Float)
animation.getAnimatedValue();
pathMeasure.getPosTan(value, mCurrentPositionOne, null);
我們可以看見(jiàn)把一個(gè)二維數(shù)組放到了getPosTan這個(gè)方法里面,然后還有一個(gè)animation,這里的animation來(lái)自哪里呢?來(lái)自這里:valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 獲取當(dāng)前點(diǎn)坐標(biāo)封裝到mCurrentPosition
float value = (Float) animation.getAnimatedValue();
pathMeasure.getPosTan(value, mCurrentPositionOne, null);
postInvalidate();
}
});
看見(jiàn)沒(méi),是動(dòng)畫(huà)的監(jiān)聽(tīng)里面來(lái)的,getPosTan的最后一個(gè)參數(shù)通常也就寫(xiě)null就行了,那么這整個(gè)一行的代碼意思就是當(dāng)動(dòng)畫(huà)發(fā)生了變化,就執(zhí)行這行代碼,然后這行代碼會(huì)把這個(gè)時(shí)間點(diǎn)的路徑上的坐標(biāo)賦值給mCurrentPositionOne。
那我們獲取到看這個(gè)路徑上的坐標(biāo)點(diǎn)怎么辦呢?
立馬用來(lái)ondraw里面啊,我的小球此時(shí)就可以根據(jù)這個(gè)坐標(biāo)點(diǎn)去繪制自己的位置,這個(gè)的話,當(dāng)動(dòng)畫(huà)開(kāi)始時(shí),小球就會(huì)不斷接受新的坐標(biāo),然后不斷重繪,最終產(chǎn)生旋轉(zhuǎn)小球的效果。
我先把屬性動(dòng)畫(huà)的代碼貼出來(lái):
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)前點(diǎn)坐標(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();
我寫(xiě)了個(gè)800,也就是動(dòng)畫(huà)的維持時(shí)間,但是我們發(fā)現(xiàn)有啊后幾個(gè)小球,所以我們需要繪制好幾個(gè)小球,然后給他們不同的動(dòng)畫(huà),為什么呢?因?yàn)閯?dòng)畫(huà)都一樣,小球就疊加在一起了,我們就只能看見(jiàn)一個(gè)球了。
說(shuō)到這里的話,我們的目標(biāo)算時(shí)完成了,具體的操作,大家參考以下代碼,或者去:android自定義View索引
里面動(dòng)畫(huà)的demo進(jìn)行下載,大家隨意,下面給出代碼:
/**
* 仿視頻加載動(dòng)畫(huà),旋轉(zhuǎn)的藍(lán)色小球
*/
public class RotaryBall extends View {
private Path rotationPath;
private float radius;
private Paint circlePaintOne;
private PathMeasure pathMeasure;
private int finishAnimateOne = 0; // 用來(lái)判斷當(dāng)前動(dòng)畫(huà)有沒(méi)有開(kāi)始
private int finishAnimateTwo = 0; // 用來(lái)判斷當(dāng)前動(dòng)畫(huà)有沒(méi)有開(kāi)始
private int finishAnimateThree = 0; // 用來(lái)判斷當(dāng)前動(dòng)畫(huà)有沒(méi)有開(kāi)始
private int finishAnimateFour = 0; // 用來(lái)判斷當(dāng)前動(dòng)畫(huà)有沒(méi)有開(kāi)始
private int finishAnimateFive = 0; // 用來(lái)判斷當(dāng)前動(dòng)畫(huà)有沒(méi)有開(kāi)始
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表示第一次運(yùn)行,0表示動(dòng)畫(huà)結(jié)束或者沒(méi)開(kāi)始,1表示正在運(yùn)動(dòng)中
private boolean animateOrNot = true; //用來(lái)決定是否開(kāi)啟動(dòng)畫(huà)
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() { //用線程來(lái)統(tǒng)一五個(gè)圓的周期
@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)用,開(kāi)始動(dòng)畫(huà)
public void startAnimate() {
if (!animateOrNot) {
animateOrNot = true;
currentStatus = -1;
invalidate();
}
}
//供外部調(diào)用,停止動(dòng)畫(huà)
public void stopAnimate() {
if (animateOrNot) {
animateOrNot = false;
}
}
//界面被銷(xiāo)毀
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopAnimate();
clearAllAnimation();
}
//清除所有動(dòng)畫(huà)效果
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;
}
}
//開(kāi)始第一個(gè)小球的動(dòng)畫(huà)
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)前點(diǎn)坐標(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();
}
//開(kāi)始第二個(gè)小球的動(dòng)畫(huà)
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)前點(diǎn)坐標(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();
}
//開(kāi)始第三個(gè)小球的動(dòng)畫(huà)
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)前點(diǎn)坐標(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();
}
//開(kāi)始第四個(gè)小球的動(dòng)畫(huà)
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)前點(diǎn)坐標(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();
}
//開(kāi)始第五個(gè)小球的動(dòng)畫(huà)
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)前點(diǎn)坐標(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)小球動(dòng)畫(huà)實(shí)例代碼,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android Tween動(dòng)畫(huà)之RotateAnimation實(shí)現(xiàn)圖片不停旋轉(zhuǎn)效果實(shí)例介紹
- Android開(kāi)發(fā)之圖形圖像與動(dòng)畫(huà)(二)Animation實(shí)現(xiàn)圖像的漸變/縮放/位移/旋轉(zhuǎn)
- Android編程實(shí)現(xiàn)RotateAnimation設(shè)置中心點(diǎn)旋轉(zhuǎn)動(dòng)畫(huà)效果
- Android 3D旋轉(zhuǎn)動(dòng)畫(huà)效果實(shí)現(xiàn)分解
- Android動(dòng)畫(huà)之漸變動(dòng)畫(huà)(Tween Animation)詳解 (漸變、縮放、位移、旋轉(zhuǎn))
- Android酷炫動(dòng)畫(huà)效果之3D星體旋轉(zhuǎn)效果
- Android旋轉(zhuǎn)、平移、縮放和透明度漸變的補(bǔ)間動(dòng)畫(huà)
- Android使用Rotate3dAnimation實(shí)現(xiàn)3D旋轉(zhuǎn)動(dòng)畫(huà)效果的實(shí)例代碼
- Android仿餓了么加入購(gòu)物車(chē)旋轉(zhuǎn)控件自帶閃轉(zhuǎn)騰挪動(dòng)畫(huà)的按鈕效果(實(shí)例詳解)
- Android實(shí)現(xiàn)簡(jiǎn)單旋轉(zhuǎn)動(dòng)畫(huà)
相關(guān)文章
Android基于PhotoView實(shí)現(xiàn)的頭像/圓形裁剪控件
這篇文章主要給大家介紹了關(guān)于Android基于PhotoView實(shí)現(xiàn)的頭像/圓形裁剪控件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
Kotlin 使用高階函數(shù)實(shí)現(xiàn)回調(diào)方式
這篇文章主要介紹了Kotlin 使用高階函數(shù)實(shí)現(xiàn)回調(diào)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android中獲取網(wǎng)頁(yè)表單中的數(shù)據(jù)實(shí)現(xiàn)思路及代碼
在Android中獲取網(wǎng)頁(yè)里表單中的數(shù)據(jù)具體實(shí)現(xiàn)代碼如下,感興趣的各位可以參考過(guò)下哈,希望對(duì)大家有所幫助2013-06-06
Android 中ImageView的ScaleType使用方法
這篇文章主要介紹了Android 中ImageView的ScaleType使用方法的相關(guān)資料,希望通過(guò)本能幫助到大家,需要的朋友可以參考下2017-09-09
使用TransitionDrawable實(shí)現(xiàn)多張圖片淡入淡出效果
這篇文章主要為大家詳細(xì)介紹了使用TransitionDrawable實(shí)現(xiàn)多張圖片淡入淡出效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
Android自定義ViewGroup實(shí)現(xiàn)標(biāo)簽流容器FlowLayout
這篇文章主要介紹了Android自定義ViewGroup實(shí)現(xiàn)FlowLayout標(biāo)簽流容器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
實(shí)現(xiàn)Android 獲取cache緩存的目錄路徑的方法
這篇文章主要介紹了實(shí)現(xiàn)Android 獲取cache緩存的目錄路徑的方法的相關(guān)資料,這里實(shí)現(xiàn)一個(gè)靜態(tài)類(lèi)來(lái)實(shí)現(xiàn)該功能,希望能幫助到大家,需要的朋友可以參考下2017-08-08

