Android應(yīng)用中經(jīng)常需要使用到各式各樣的動(dòng)畫效果凉袱,產(chǎn)品設(shè)計(jì)總是已他們重破天機(jī)的想象給出看著就很難實(shí)現(xiàn)的動(dòng)畫张足,不過(guò)值得慶幸的是Android的動(dòng)畫框架也在不斷地進(jìn)化以滿足我們的需求器瘪。Android發(fā)展到現(xiàn)在绅项,有多少種可以使用的動(dòng)畫類型呢赁咙,在本篇文章中將給出一些例子來(lái)給出答案今野。
在Android3.0之前的牙寞,我們可以使用的動(dòng)畫類型僅僅有兩種饺鹃,只能滿足一些簡(jiǎn)單的功能效果,他們分別是逐幀動(dòng)畫和補(bǔ)間動(dòng)畫碎税,而在Android3.0時(shí)尤慰,Android SDK為開發(fā)者帶來(lái)了強(qiáng)大而又靈活的屬性動(dòng)畫Property Animation(聽(tīng)上去就好高大上啊)雷蹂,可以稱之為動(dòng)畫界的重大突破伟端,它可以只通過(guò)簡(jiǎn)單的api調(diào)用就實(shí)現(xiàn)較為復(fù)雜的動(dòng)畫效果。而隨著時(shí)間的推進(jìn)啊匪煌,在Android4.4時(shí)责蝠,又為開發(fā)者帶來(lái)了android.transtion框架(基本常用的動(dòng)畫效果都給你封裝好了,你就偷著樂(lè)吧)萎庭,我們通過(guò)框架可以更直觀的定義動(dòng)畫效果霜医,接下來(lái)我們簡(jiǎn)單介紹一下除屬性動(dòng)畫之外的三種動(dòng)畫類型(因?yàn)閷傩詣?dòng)畫比較牛逼,一定要重點(diǎn)講解驳规,目前在一些自定義View中肴敛,使用一些動(dòng)畫效果都是使用屬性動(dòng)畫)。
1.1 逐幀動(dòng)畫(Frame Animation)
逐幀動(dòng)畫也叫Drawable Animation吗购,是最簡(jiǎn)單最基本的動(dòng)畫類型了医男,他就是利用人眼的視覺(jué)暫留效應(yīng)——也就是光對(duì)視網(wǎng)膜所產(chǎn)生的視覺(jué)在光停止作用后也會(huì)保留一段時(shí)間的現(xiàn)象。那意思就是說(shuō)捻勉,逐幀動(dòng)畫就是一幀一幀的圖像咯镀梭!是的沒(méi)錯(cuò),設(shè)計(jì)師給出一系列狀態(tài)不斷變化的圖片踱启,開發(fā)者可以制定動(dòng)畫中每一幀對(duì)于的圖片和持續(xù)的時(shí)間报账,然后就可以播放幀動(dòng)畫了研底。下面我們通過(guò)xml的代碼的形式分別實(shí)現(xiàn)類似物流配送時(shí)的動(dòng)畫效果:
![image.png](http://upload-images.jianshu.io/upload_images/5815336- 5f61db40219e3da9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
圖片資源放一下:
1.1.1:xml方式實(shí)現(xiàn)逐幀動(dòng)畫
這是實(shí)現(xiàn)逐幀動(dòng)畫最常用的方式,首先我們將每一幀的圖片放入drawable目錄下透罢,然后在res/drawable目錄中新建一個(gè)xml文件榜晦,在這里文件中根目錄使用<animation-list>標(biāo)簽來(lái)定義幀動(dòng)畫執(zhí)行順序,在其中不僅可以指定每一幀的圖片琐凭,還可以指定動(dòng)畫持續(xù)時(shí)間等芽隆。具體如下:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<!--oneshot是否重復(fù)執(zhí)行動(dòng)畫,false表示只執(zhí)行一次,true表示執(zhí)行
item幀動(dòng)畫順序统屈,drawable幀圖片胚吁,duration動(dòng)畫持續(xù)時(shí)長(zhǎng)-->
<item android:drawable="@drawable/a" android:duration="150"></item>
<item android:drawable="@drawable/b" android:duration="150"></item>
<item android:drawable="@drawable/c" android:duration="150"></item>
<item android:drawable="@drawable/d" android:duration="150"></item>
</animation-list>
之后我們只需要在xml中創(chuàng)建一個(gè)ImageView控件,設(shè)置其背景為剛才drawable目錄下定義的xml文件:
··
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.xjf.androidanimation.MainActivity">
<ImageView
android:id="@+id/iv"
android:layout_width="60dp"
android:layout_height="40dp"
android:background="@drawable/frame_anim"
android:layout_gravity="center" />
</LinearLayout>
最后記得在Java中獲取到AnimationDrawable對(duì)象愁憔,調(diào)用start方法開始執(zhí)行動(dòng)畫:
ImageView iv = findViewById(R.id.iv);
AnimationDrawable animationDrawable = (AnimationDrawable) iv.getBackground();
animationDrawable.start();
1.1.2:代碼方式:
ImageView imageView = findViewById(R.id.iv);
AnimationDrawable animationDrawable = new AnimationDrawable();
Drawable a = getResources().getDrawable(R.drawable.a);
Drawable b = getResources().getDrawable(R.drawable.b);
Drawable c = getResources().getDrawable(R.drawable.c);
Drawable d = getResources().getDrawable(R.drawable.d);
animationDrawable.addFrame(a,120);
animationDrawable.addFrame(b,120);
animationDrawable.addFrame(c,120);
animationDrawable.addFrame(d,120);
imageView.setBackgroundDrawable(animationDrawable);
animationDrawable.setOneShot(false);
//動(dòng)畫開始
animationDrawable.start();
//停止
animationDrawable.stop();
1.2:補(bǔ)間動(dòng)畫(Tween Animation)
補(bǔ)間動(dòng)畫不需要用戶定義動(dòng)畫啊執(zhí)行過(guò)程的每一幀腕扶,只需要定義動(dòng)畫開始和結(jié)束這兩個(gè)關(guān)鍵幀抡谐,并指定動(dòng)畫的變化時(shí)間和方式即可丰包,之后交給Android系統(tǒng)進(jìn)行計(jì)算镀岛,通過(guò)在兩個(gè)幀之間插入漸變值來(lái)實(shí)現(xiàn)平滑過(guò)渡苞轿,從而對(duì)View的內(nèi)容完成一系列的圖形變換來(lái)實(shí)現(xiàn)動(dòng)畫效果,它主要包括四種動(dòng)畫:透明度變化Alpha袍冷,大小變化Scale念秧,位移變化Translate及旋轉(zhuǎn)變化Rotate鸽心,這四種動(dòng)畫也可以組合秋茫,從而生成更加炫酷的效果史简,同樣定義補(bǔ)間動(dòng)畫也有XML和代碼兩種方式可以實(shí)現(xiàn),在了解動(dòng)畫之前肛著,我們有必要搞清楚插值器Interpolator圆兵。
1.2.1 插值器Interpolator
上一段落有簡(jiǎn)單提到Android系統(tǒng)會(huì)在補(bǔ)間動(dòng)畫的開始和結(jié)束幀直接插入漸變值,它依據(jù)的便是Interpolator枢贿,具體來(lái)說(shuō)殉农,Interpolator會(huì)根據(jù)類型的不同,選擇不同的算法計(jì)算出在補(bǔ)間動(dòng)畫期間所需要?jiǎng)討B(tài)插入幀的密度和位置局荚,Interpolator負(fù)責(zé)控制動(dòng)畫的變化速度超凳,使得前面所說(shuō)的四種基本動(dòng)畫能夠以勻速、加速耀态、減速轮傍、拋物線等多種效果進(jìn)行變化。
具體到Android代碼中茫陆,你可以發(fā)現(xiàn)Interpolator類是一個(gè)空接口金麸,它繼承自TimeInterpolator擎析,TimeInterpolator時(shí)間插值器允許動(dòng)畫進(jìn)行非線性運(yùn)動(dòng)變換簿盅,如加速或減速等挥下,該接口中世紀(jì)中有一個(gè)方法,入?yún)⑹?.0-1.0的一個(gè)值桨醋,返回值可以小于0也可以大于1棚瘟。
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);}
AndroidSDK默認(rèn)提供了幾個(gè)Interpolator的實(shí)現(xiàn)類來(lái)滿足我們的基本需求:
意義如下:
AccelerateDecelerateInterpolator 在動(dòng)畫開始與介紹的地方速率改變比較慢,在中間的時(shí)候加速
AccelerateInterpolator 在動(dòng)畫開始的地方速率改變比較慢喜最,然后開始加速
AnticipateInterpolator 開始的時(shí)候向后然后向前甩
AnticipateOvershootInterpolator 開始的時(shí)候向后然后向前甩一定值后返回最后的值
BounceInterpolator 動(dòng)畫結(jié)束的時(shí)候彈起
CycleInterpolator 動(dòng)畫循環(huán)播放特定的次數(shù)偎蘸,速率改變沿著正弦曲線
DecelerateInterpolator 在動(dòng)畫開始的地方快然后慢
LinearInterpolator 以常量速率改變
OvershootInterpolator 向前甩一定值后再回到原來(lái)位置
如果上述的默認(rèn)插值器不符合我們項(xiàng)目的實(shí)際需求,開發(fā)者也可以通過(guò)實(shí)現(xiàn)Interpolator接口來(lái)編寫自己的插值器瞬内。
好了迷雪,關(guān)于插值器的介紹就到這吧,具體的效果朋友們可以在自己編寫的Tween Animation中加入不同的插值器去了解虫蝶。
1.3 補(bǔ)間動(dòng)畫Tween Animation具體實(shí)現(xiàn):
首先我們看一下章咧,系統(tǒng)提供了哪些:
然后了解一下他們各自的效果(其實(shí)就是我們上面所講的四種動(dòng)畫類型了):
1.3.1:AlphaAnimation(透明度動(dòng)畫效果)
xml方式定義:在res/anim(沒(méi)有anim就自己新建一個(gè))中新建一個(gè)xml文件,根元素為set:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
>
<translate
android:fromYDelta="100%p"
android:toYDelta="0"></translate>
<alpha android:fromAlpha="0.0"
android:toAlpha="1.0"></alpha>
</set>
在Java代碼中:
//設(shè)置透明度變化為0-1
AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
//設(shè)置動(dòng)畫時(shí)長(zhǎng)
alphaAnimation.setDuration(200);
//創(chuàng)建插值器
AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();
//插值器和動(dòng)畫綁定
alphaAnimation.setInterpolator(interpolator);
//設(shè)置動(dòng)畫結(jié)束后保留結(jié)束狀態(tài)
alphaAnimation.setFillAfter(true);
imageView.setAnimation(alphaAnimation);
AlphaAnimation動(dòng)畫的構(gòu)造方法只有一個(gè):(參數(shù)分別是初始的透明度和結(jié)束的透明度)
/**
* Constructor to use when building an AlphaAnimation from code
*
* @param fromAlpha Starting alpha value for the animation, where 1.0 means
* fully opaque and 0.0 means fully transparent.
* @param toAlpha Ending alpha value for the animation.
*/
public AlphaAnimation(float fromAlpha, float toAlpha) {
mFromAlpha = fromAlpha;
mToAlpha = toAlpha; }
1.3.2:ScaleAnimation
xml方式:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:duration="200">
<scale
android:fromXScale="0.2"
android:fromYScale="0.2"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="1.5"></scale>
</set>
Java代碼:
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f,4.0f,1.0f,4.0f, Animation.RELATIVE_TO_SELF,0.0f,Animation.RELATIVE_TO_SELF,0.0f);
scaleAnimation.setDuration(200);
scaleAnimation.setFillAfter(true);
imageView.setAnimation(scaleAnimation);
ScaleAnimation的構(gòu)造方法就比較多啦:
public ScaleAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScaleAnimation(float fromX, float toX, float fromY, float toY) {
}
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
float pivotX, float pivotY) {
}
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
int pivotXType, float pivotXValue, int pivotYType, float pivotYValue
}
可以看到入?yún)⒎浅6嗄苷妫覀儊?lái)看一下各參數(shù)的含義:
float fromX 動(dòng)畫起始時(shí) X坐標(biāo)上的伸縮尺寸
float toX 動(dòng)畫結(jié)束時(shí) X坐標(biāo)上的伸縮尺寸
float fromY 動(dòng)畫起始時(shí)Y坐標(biāo)上的伸縮尺寸
float toY 動(dòng)畫結(jié)束時(shí)Y坐標(biāo)上的伸縮尺寸
int pivotXType 動(dòng)畫在X軸相對(duì)于物件位置類型
float pivotXValue 動(dòng)畫相對(duì)于物件的X坐標(biāo)的開始位置
int pivotYType 動(dòng)畫在Y軸相對(duì)于物件位置類型
float pivotYValue 動(dòng)畫相對(duì)于物件的Y坐標(biāo)的開始位置
1.3.3 translateAnimation
xml實(shí)現(xiàn):
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="1000"></translate>
</set>
JAVA代碼實(shí)現(xiàn):
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f,Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f);
scaleAnimation.setDuration(200);
scaleAnimation.setFillAfter(true);
imageView.setAnimation(scaleAnimation);
構(gòu)造方法:
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
}
public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
int fromYType, float fromYValue, int toYType, float toYValue) {
}
構(gòu)造方法各參數(shù)含義:
參數(shù)fromXType:開始時(shí)x軸相對(duì)于組件的位置類型赁严。
參數(shù)fromXValue:開始時(shí)x軸的坐標(biāo)。根據(jù)fromXType代表不同的意義粉铐。
參數(shù)toXType:結(jié)束時(shí)x軸相對(duì)于組件的位置類型疼约。
參數(shù)toXValue:結(jié)束時(shí)x軸的坐標(biāo)。根據(jù)toXType代表不同的意義蝙泼。
參數(shù)fromYType:開始時(shí)y軸相對(duì)于組件的位置類型程剥。
參數(shù)fromYValue:開始時(shí)y軸的坐標(biāo)。根據(jù)fromYType代表不同的意義踱承。
參數(shù)toYType:結(jié)束時(shí)y軸相對(duì)于組件的位置類型倡缠。
參數(shù)toYValue:結(jié)束時(shí)y軸的坐標(biāo)。根據(jù)toYType代表不同的意義茎活。
關(guān)于入?yún)r(shí)的參數(shù)值:
1.3.4:RorateAnimation
XML方式:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="restart"
android:startOffset="0">
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360"
></rotate>
</set>
Java代碼實(shí)現(xiàn):
RotateAnimation rotateAnimation = new RotateAnimation(0,-720,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
scaleAnimation.setDuration(1000);
scaleAnimation.setFillAfter(true);
imageView.setAnimation(scaleAnimation);
構(gòu)造方法:
public RotateAnimation(float fromDegrees, float toDegrees) {
}
public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {
}
public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue) {
}
參數(shù):
fromDegrees:旋轉(zhuǎn)的起始角度
toDegrees:旋轉(zhuǎn)的終止角度
pivotX:該對(duì)象被旋轉(zhuǎn)的點(diǎn)的X坐標(biāo)昙沦,指定為一個(gè)絕對(duì)數(shù)字,其中0是左邊緣载荔。
pivotY:對(duì)象被旋轉(zhuǎn)的點(diǎn)的Y坐標(biāo)盾饮,指定為一個(gè)絕對(duì)數(shù)字,其中0是頂部邊緣懒熙。
1.3.5自定義補(bǔ)間動(dòng)畫
在實(shí)際開發(fā)中丘损,可能會(huì)遇到四種基本動(dòng)畫無(wú)法實(shí)現(xiàn)的需求,這時(shí)可以自定義補(bǔ)間動(dòng)畫工扎,只需要繼承Animation徘钥,重寫這個(gè)抽象基類中的applyTransformation方法:
public class MyAnimtion extends Animation{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
}
}
interpolator表示動(dòng)畫的時(shí)間進(jìn)行比,值在0-1變化肢娘,第二個(gè)參數(shù)表示補(bǔ)間動(dòng)畫在不同時(shí)刻對(duì)View的變形程度.
對(duì)于Tween Animation的介紹就到此告一段落呈础,有想看各動(dòng)畫插值器效果和了解具體自定義補(bǔ)間動(dòng)畫的朋友可參考博客:http://blog.csdn.net/u011043551/article/details/65443751
1.4 屬性動(dòng)畫(Property Animation)
這是本篇文章的重點(diǎn),開發(fā)必考題
屬性動(dòng)畫是在Android3.0時(shí)引入的舆驶,在補(bǔ)間動(dòng)畫中,我們只能改變View的繪制效果而钞,View的真實(shí)屬性是沒(méi)有改變的沙廉,而屬性動(dòng)畫則可以直接改變View對(duì)象的屬性值,同時(shí)屬性動(dòng)畫幾乎可以對(duì)任何對(duì)象執(zhí)行動(dòng)畫臼节,而不是局限在View對(duì)象上撬陵。
首先簡(jiǎn)單了解一下相關(guān)屬性:
Duration動(dòng)畫的持續(xù)時(shí)間,默認(rèn)300ms网缝。
Time interpolation:時(shí)間差值巨税,乍一看不知道是什么,但是我說(shuō)LinearInterpolator粉臊、AccelerateDecelerateInterpolator垢夹,大家一定知道是干嘛的了,定義動(dòng)畫的變化率维费。
Repeat count and behavior:重復(fù)次數(shù)果元、以及重復(fù)模式;可以定義重復(fù)多少次犀盟;重復(fù)時(shí)從頭開始而晒,還是反向。
Animator sets: 動(dòng)畫集合阅畴,你可以定義一組動(dòng)畫倡怎,一起執(zhí)行或者順序執(zhí)行。
Frame refresh delay:幀刷新延遲贱枣,對(duì)于你的動(dòng)畫监署,多久刷新一次幀;默認(rèn)為10ms纽哥,但最終依賴系統(tǒng)的當(dāng)前狀態(tài)钠乏;基本不用管。
相關(guān)的類
ObjectAnimator 動(dòng)畫的執(zhí)行類春塌,后面詳細(xì)介紹
ValueAnimator 動(dòng)畫的執(zhí)行類晓避,后面詳細(xì)介紹
AnimatorSet 用于控制一組動(dòng)畫的執(zhí)行:線性,一起只壳,每個(gè)動(dòng)畫的先后執(zhí)行等俏拱。
AnimatorInflater 用戶加載屬性動(dòng)畫的xml文件
TypeEvaluator 類型估值,主要用于設(shè)置動(dòng)畫操作屬性的值吼句。
TimeInterpolator 時(shí)間插值锅必,上面已經(jīng)介紹。
總的來(lái)說(shuō)惕艳,屬性動(dòng)畫就是搞隐,動(dòng)畫的執(zhí)行類來(lái)設(shè)置動(dòng)畫操作的對(duì)象的屬性分蓖、持續(xù)時(shí)間,開始和結(jié)束的屬性值尔许,時(shí)間差值等,然后系統(tǒng)會(huì)根據(jù)設(shè)置的參數(shù)動(dòng)態(tài)的變化對(duì)象的屬性终娃。
屬性動(dòng)畫的基類是Animator味廊,它是一個(gè)抽象類,我們不會(huì)直接使用這個(gè)類棠耕,通常都是繼承它并重寫其中的相關(guān)方法余佛,Android SDK為開發(fā)者默認(rèn)提供了幾個(gè)子類,大多數(shù)情況下使用這些子類就可以完成開發(fā)任務(wù)了窍荧。
1.4.1:Evaluator
在介紹Animator之前辉巡,我們先來(lái)了解一個(gè)名為Evaluator的概念,它是用來(lái)控制屬性動(dòng)畫如何計(jì)算屬性值的蕊退,接口定義是TypeEvaluator郊楣,其中定義了evaluate方法,供不同類型的子類實(shí)現(xiàn)瓤荔。
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
而系統(tǒng)也有提供了一些常見(jiàn)的實(shí)現(xiàn)類净蚤,比如IntEvaluator、FloateEvaluator输硝、ArghEvaluator等今瀑。我們來(lái)看一下ArgbEvaluator的具體實(shí)現(xiàn),可以看到實(shí)現(xiàn)邏輯很簡(jiǎn)單点把,就是根據(jù)輸入的初始值和結(jié)束值及一個(gè)進(jìn)度比橘荠,計(jì)算出每一個(gè)進(jìn)度對(duì)于的ARGB值。
public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();
/**
* Returns an instance of <code>ArgbEvaluator</code> that may be used in
* {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
* be used in multiple <code>Animator</code>s because it holds no state.
* @return An instance of <code>ArgbEvalutor</code>.
*
* @hide
*/
public static ArgbEvaluator getInstance() {
return sInstance;
}
/**
* This function returns the calculated in-between value for a color
* given integers that represent the start and end values in the four
* bytes of the 32-bit int. Each channel is separately linearly interpolated
* and the resulting calculated values are recombined into the return value.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @param endValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @return A value that is calculated to be the linearly interpolated
* result, derived by separating the start and end values into separate
* color channels and interpolating each one separately, recombining the
* resulting values in the same way.
*/
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
}
1.4.2:AnimatorSet
AnimatorSet是Animator的子類郎逃,用來(lái)組合多個(gè)Animator哥童,并指定這些Animator是順序播放還是同時(shí)播放。
1.4.3:ValueAnimator:
ValueAnimator是屬性動(dòng)畫最重要的一個(gè)類褒翰,繼承自Animator如蚜,它定義了屬性動(dòng)畫大部分的核心功能,包括計(jì)算各個(gè)幀的屬性值影暴,處理更新事件错邦,按照屬性值的類型控制計(jì)算規(guī)則等。
一個(gè)完整的屬性動(dòng)畫由以下兩部分組成型宙。
計(jì)算動(dòng)畫各個(gè)幀的相關(guān)屬性值
將這些屬性設(shè)置給指定的對(duì)象
ValueAnimator為開發(fā)者實(shí)現(xiàn)了第一部分的功能撬呢,第二部分功能由開發(fā)者自行設(shè)置,ValueAnimator的構(gòu)造函數(shù)是空實(shí)現(xiàn)妆兑,一般都是使用如下的靜態(tài)工程方法來(lái)進(jìn)行實(shí)例化魂拦。
public ValueAnimator() {
}
public static ValueAnimator ofArgb(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
anim.setEvaluator(ArgbEvaluator.getInstance());
return anim;
}
public static ValueAnimator ofFloat(float... values) {
ValueAnimator anim = new ValueAnimator();
anim.setFloatValues(values);
return anim;
}
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
ValueAnimator anim = new ValueAnimator();
anim.setValues(values);
return anim;
}
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
ValueAnimator anim = new ValueAnimator();
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
獲取到實(shí)例后毛仪,接著需要設(shè)置動(dòng)畫持續(xù)時(shí)間、插值方式芯勘、重復(fù)次數(shù)等屬性值箱靴,然后啟動(dòng)動(dòng)畫,最后還需要為ValueAnimator注冊(cè)AnimatorUpdateListenner監(jiān)聽(tīng)器荷愕,并在這個(gè)監(jiān)聽(tīng)器的onAnimationUpdate方法中將計(jì)算出來(lái)的值設(shè)置給對(duì)象衡怀。我們從React Native這個(gè)開源框架中可以看到如下用法:
1.4.4 ObjectAnimator
ObjectAnimator是ValueAnimator的子類,封裝實(shí)現(xiàn)了上面所說(shuō)的第二部分的功能安疗。因此抛杨,在實(shí)際開發(fā)中用到最多的就是ObjectAnimator,只有在ObjectAnimator實(shí)現(xiàn)不了的場(chǎng)景下荐类,才考慮使用ValueAnimator怖现。ObjectAnimator和ValueAnimator在構(gòu)造實(shí)例時(shí)最大的不同是指需要指定動(dòng)畫作用的具體對(duì)象和對(duì)象屬性名,而且一般不需要注冊(cè)AnimatorUpdateListener監(jiān)聽(tīng)器玉罐,簡(jiǎn)單的旋轉(zhuǎn)View實(shí)例如下:
public void rotateyAnimRun(View view)
{
ObjectAnimator//
.ofFloat(view, "rotationX", 0.0F, 360.0F)//
.setDuration(500)//
.start();
}
那么對(duì)于屬性動(dòng)畫簡(jiǎn)單介紹就到此告一段落屈嗤,我們使用的越多就越能了解到它的強(qiáng)大之處,上面主要是講述屬性動(dòng)畫一些概念和簡(jiǎn)單實(shí)用方式吊输,而接下來(lái)我們通過(guò)幾個(gè)demo來(lái)知道屬性動(dòng)畫還可以怎么用恢共,實(shí)現(xiàn)什么樣的效果。
實(shí)際開發(fā)當(dāng)中璧亚,我們使用一下ViewPropertyAnimator來(lái)實(shí)現(xiàn)各種動(dòng)畫效果讨韭,而且是在是簡(jiǎn)單到難以想象。
使用方式:view.animate()后直接跟translation和alpha等想實(shí)現(xiàn)的效果
ImageView imageView = findViewById(R.id.iv);
imageView.animate().translationX(500).translationY(700).alpha(1f).setDuration(1000 );
我們看看實(shí)際操作方法有哪些癣蟋?
話不多說(shuō)先寫一個(gè)出自Hencoder的Demo
public class SportView extends View{
public float getProgress() {
return progress;
}
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
float progress = 0;
RectF arcRectF = new RectF();
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public SportView(Context context) {
this(context,null);
}
public SportView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public SportView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
paint.setTextSize(Utils.dpToPixel(30));
paint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//圓心位置
float centerX = getWidth()/2;
float centerY = getHeight()/2;
//注意因?yàn)閳A弧也有高度的原因透硝,半徑應(yīng)該取設(shè)置小于寬度的一半
float radius = getWidth()/5*2;
paint.setColor(Color.parseColor("#E91E63"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(Utils.dpToPixel(10));
//設(shè)置區(qū)域,四個(gè)參數(shù)對(duì)應(yīng)四個(gè)點(diǎn)坐標(biāo)
arcRectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
canvas.drawArc(arcRectF, 135, progress*2.7f, false, paint);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
canvas.drawText((int)progress+"%",centerX,centerY-(paint.ascent()+paint.descent())/5*2,paint);
}}
xml中直接引用我們的自定義控件:
<com.example.xjf.androidanimation.SportView
android:id="@+id/sportView"
android:layout_width="160dp"
android:layout_height="160dp" />
最后在Activity中直接創(chuàng)建一個(gè)ObjectAnimator對(duì)象并傳入我們的進(jìn)度參數(shù)綁定View:
SportView sportView = findViewById(R.id.sportView);
ObjectAnimator animator = ObjectAnimator.ofFloat(sportView,"progress",0,65);
animator.start();
通用功能
- setDuration(int duration) 設(shè)置動(dòng)畫時(shí)長(zhǎng)
單位是毫秒疯搅。
// imageView1: 500 毫秒
imageView1.animate() [圖片上傳中...(AccelerateDecelerateInterpolator.gif-277ef4-1514647894330-0)]
.translationX(500)
.setDuration(500);
// imageView2: 2 秒
ObjectAnimator animator = ObjectAnimator.ofFloat(
imageView2, "translationX", 500);
animator.setDuration(2000);
animator.start();
- setInterpolator(Interpolator interpolator) 設(shè)置 Interpolator
// imageView1: 線性 Interpolator濒生,勻速
imageView1.animate()
.translationX(500)
.setInterpolator(new LinearInterpolator());
// imageView: 帶施法前搖和回彈的 Interpolator
ObjectAnimator animator = ObjectAnimator.ofFloat(
imageView2, "translationX", 500);
animator.setInterpolator(new AnticipateOvershootInterpolator());
animator.start();
接下來(lái)簡(jiǎn)單介紹一下每一個(gè)Interpolator:
AccelerateDecelerateInterpolator
先加速再減速。這是默認(rèn)的 Interpolator幔欧,也就是說(shuō)如果你不設(shè)置的話罪治,那么動(dòng)畫將會(huì)使用這個(gè) Interpolator。
這個(gè)是一種最符合現(xiàn)實(shí)中物體運(yùn)動(dòng)的 Interpolator礁蔗,它的動(dòng)畫效果看起來(lái)就像是物體從速度為 0 開始逐漸加速觉义,然后再逐漸減速直到 0 的運(yùn)動(dòng)。它的速度 / 時(shí)間曲線以及動(dòng)畫完成度 / 時(shí)間曲線都是一條正弦 / 余弦曲線浴井。具體的效果如下:
LinearInterpolator
勻速晒骇。
AccelerateInterpolator
持續(xù)加速。
在整個(gè)動(dòng)畫過(guò)程中,一直在加速洪囤,直到動(dòng)畫結(jié)束的一瞬間徒坡,直接停止。
別看見(jiàn)它加速驟停就覺(jué)得這是個(gè)神經(jīng)病模型哦瘤缩,它很有用的喇完。它主要用在離場(chǎng)效果中,比如某個(gè)物體從界面中飛離剥啤,就可以用這種效果锦溪。它給人的感覺(jué)就會(huì)是「這貨從零起步,加速飛走了」铐殃。到了最后動(dòng)畫驟停的時(shí)候,物體已經(jīng)飛出用戶視野跨新,看不到了富腊,所以他們是并不會(huì)察覺(jué)到這個(gè)驟停的。
DecelerateInterpolator
持續(xù)減速直到 0域帐。
動(dòng)畫開始的時(shí)候是最高速度赘被,然后在動(dòng)畫過(guò)程中逐漸減速,直到動(dòng)畫結(jié)束的時(shí)候恰好減速到 0肖揣。它的效果和上面這個(gè) AccelerateInterpolator 相反民假,適用場(chǎng)景也和它相反:它主要用于入場(chǎng)效果,比如某個(gè)物體從界面的外部飛入界面后停在某處龙优。它給人的感覺(jué)會(huì)是「咦飛進(jìn)來(lái)個(gè)東西羊异,讓我仔細(xì)看看,哦原來(lái)是 XXX」彤断。
AnticipateInterpolator
先回拉一下再進(jìn)行正常動(dòng)畫軌跡野舶。效果看起來(lái)有點(diǎn)像投擲物體或跳躍等動(dòng)作前的蓄力。如果是圖中這樣的平移動(dòng)畫宰衙,那么就是位置上的回拉平道;如果是放大動(dòng)畫,那么就是先縮小一下再放大供炼;其他類型的動(dòng)畫同理一屋。
這個(gè) Interpolator 就有點(diǎn)耍花樣了袋哼。沒(méi)有通用的適用場(chǎng)景冀墨,根據(jù)具體需求和設(shè)計(jì)師的偏好而定。
OvershootInterpolator
動(dòng)畫會(huì)超過(guò)目標(biāo)值一些涛贯,然后再?gòu)椈貋?lái)轧苫。效果看起來(lái)有點(diǎn)像你一屁股坐在沙發(fā)上后又被彈起來(lái)一點(diǎn)的感覺(jué)。
BounceInterpolator
在目標(biāo)值處彈跳。有點(diǎn)像玻璃球掉在地板上的效果含懊。
CycleInterpolator
這個(gè)也是一個(gè)正弦 / 余弦曲線身冬,不過(guò)它和 AccelerateDecelerateInterpolator 的區(qū)別是,它可以自定義曲線的周期岔乔,所以動(dòng)畫可以不到終點(diǎn)就結(jié)束酥筝,也可以到達(dá)終點(diǎn)后回彈,回彈的次數(shù)由曲線的周期決定雏门,曲線的周期由 CycleInterpolator() 構(gòu)造方法的參數(shù)決定嘿歌。
參數(shù)為 0.5f:參數(shù)為2f:
PathInterpolator
自定義動(dòng)畫完成度 / 時(shí)間完成度曲線。
用這個(gè) Interpolator 你可以定制出任何你想要的速度模型茁影。定制的方式是使用一個(gè) Path 對(duì)象來(lái)繪制出你要的動(dòng)畫完成度 / 時(shí)間完成度曲線宙帝。例如:
Path interpolatorPath = new Path();
...
// 勻速
interpolatorPath.lineTo(1, 1);
Path interpolatorPath = new Path();
...
// 先以「動(dòng)畫完成度 : 時(shí)間完成度 = 1 : 1」的速度勻速運(yùn)行 25%
interpolatorPath.lineTo(0.25f, 0.25f);
// 然后瞬間跳躍到 150% 的動(dòng)畫完成度
interpolatorPath.moveTo(0.25f, 1.5f);
// 再勻速倒車,返回到目標(biāo)點(diǎn)
interpolatorPath.lineTo(1, 1);
本篇文章到此結(jié)束啦募闲,感謝Hencoder開源的自定義View學(xué)習(xí)資料以及Android高級(jí)進(jìn)階一書所講解的知識(shí)步脓。本文都是出自上面文章,自己算作學(xué)習(xí)資料吧浩螺,下次更新一個(gè)比較完整的動(dòng)畫效果來(lái)總結(jié)一下靴患,以及補(bǔ)上5.0之后的一些動(dòng)畫新特性,再見(jiàn)要出。
感謝:http://hencoder.com/
感謝:Android高級(jí)進(jìn)階