編輯:關於Android編程
Android提供了三種動畫機制:屬性動畫(property animation)、補間動畫(tween animation / view animation)、幀動畫(drawable animation / frame animation)。其中屬性動畫被官方推薦使用,因為它擁有更多的靈活性和特色(because it is more flexible and offers more features)、並可以幾乎完全替代後兩種動畫。
本文將介紹Android中的動畫機制。如需訪問官方原文,您可以點擊這些鏈接:
《Property Animation》
《View Animation》
《Drawable Animation》
屬性動畫在Android 3.0(API 11)版本中引入,該動畫可以作用在任何對象的屬性上( property animation system lets you animate properties of any object),甚至包括未在屏幕上渲染的對象(including ones that are not rendered to the screen)。屬性動畫具有很高的擴展性、您可以將動畫作用在定制的屬性上。
屬性動畫可以實現幾乎任何動畫效果。該動畫的原理是在一定時間內改變某個對象的屬性值來實現動畫的漸變。
使用屬性動畫可以指定的參數如下:
持續時間(Duration):您可以指定動畫的持續時間,默認為300毫秒。
時間插值器(Time interpolation):在規定的持續時間內,動畫控制的屬性值是以怎樣的速率變化的(如線性、加速 等)。
重復的次數和行為(Repeat count and behavior):您可以指定是否讓動畫重復執行、以及重復的次數。您也可以指定是否讓動畫反向執行。
動畫集合(Animator sets):您可以將若干單獨的動畫組成一個集合,以指定的順序播放(或同時播放)。
幀的刷新頻率(Frame refresh delay):您可以為動畫指定刷新的頻率。默認是10毫秒。
下圖描述了當屬性動畫作用在某個對象的屬性”x”上時的變化情況。屬性”x”表示該對象在屏幕所在位置的橫坐標。動畫持續了40毫秒、將該屬性變化了40個像素。默認每隔10毫秒就刷新一幀,對象就橫向移動10個像素。這是使用了線性差值器,表示屬性是隨時間的推移勻速變化的。
您也可以使用非線性的差值器。下圖演示了先加速後減速的情形。顯然,在相同的時間內,“x”的變化長度在一開始時沒有在中期變化的長度多。
下圖演示了動畫的運行方式。 ValueAnimator
是整個動畫執行的核心類,它負責追蹤動畫的運行情況,比如動畫的持續時間、某個時刻下動畫所控制的屬性值是多少等。
ValueAnimator
封裝了一個TimeInterpolator
接口。該接口中唯一的方法abstract float getInterpolation(float input)
就是用於定義動畫的變化速率的。方法唯一的float型參數取值范圍是0.0-1.0,表示動畫在規定時間內運行的百分比,比如為動畫設定的持續時間為10秒,當系統回調該方法時,回傳了0.3,就表示動畫執行了3秒。方法的返回值表示根據動畫執行的百分比設置屬性的執行百分比,范圍也是0.1-1.0。比如說,方法返回了參數值,這就表示屬性值隨時間線性變化,即如果還按照上面的例子,動畫控制view的”x”屬性從0變化到100,那麼方法回傳0.3時,返回值也是0.3,也就是”x”變化了30%,即變化到了30。而方法回傳0.9時,返回值也是0.9,也就是”x”變化了90%,即變化到了90;如果方法返回的是參數值的平方,這就表示屬性值是一個加速變化的過程,按照上面例子,方法回傳0.3時,返回值就是0.3^2=0.09,也就是”x”變化了9%,即變化到了9,而方法回傳0.9時,返回值為0.9^2=0.81,也就是”x”變化了81%。
ValueAnimator
中還封裝了另一個接口 TypeEvaluator
,該接口中也只有一個方法abstract T evaluate(float fraction, T startValue, T endValue)
,參數1表示屬性值執行的百分比,范圍是0.0-1.0,也就是說,getInterpolation()
方法的返回值會傳至該參數。而參數2和參數3分別表示屬性的起始值和結束值,按照上例,這兩個值分別為0和100。而evaluate()
的返回值表示為當前的屬性設置的新的屬性值,所以返回值通常為如下形式:
T returnValue = startValue + ( T ) ( fraction * ( endValue - startValue ) ;
比如按照上例,view的”x”從0-100,執行10秒,加速變化:當執行到第9秒時,返回值為0.81,即fraction為0.81,那麼套用上面的公式,即為:
//returnValue = 81
int returnValue = 0 + ( int ) ( 0.81 * ( 100 - 0 ) ) ;
這說明x執行到了81的位置。
補間動畫只能作用於View上(稍後將介紹補間動畫),可實現的動畫效果也比較有限,只包含四種動畫效果(旋轉、縮放、平移、透明度),若需要改變view的背景,補間動畫就派不上用場了。
補間動畫的另一缺點是,它只是將view視圖改變了位置,實際的view並沒有改變,比如,將補間動畫的平移效果作用於某個view上,view的點擊事件並沒有平移,還是在原來的位置上。
屬性動畫彌補了補間動畫的不足,屬性動畫完完全全地移動了對象,動畫也可以作用在任何對象的任何屬性上,比如顏色,位置,大小 等。
當然,補間動畫也不是一無是處,它的執行效率高於屬性動畫。如果您想要實現的動畫效果補間動畫就能完成,那麼沒有必要使用屬性動畫。所以,根據不同場合來選擇合適的動畫,是最好的選擇。
屬性動畫的API位於android.animation
包中。補間動畫的API位於android.view.animation
包中,該包還包含了系統定義好的插值器interpolator,您可以在屬性動畫中直接調用這些插值器。下面的這些表介紹了屬性動畫的主要API。
抽象類Animator
是創建屬性動畫的基礎類。Animator
的主要實現類如下表所示:
ValueAnimator
這是整個屬性動畫的核心類,它封裝了為動畫設置起始值、結束值、持續時間以及如何重復等方法 ,當然還有監聽變化值的回調方法。但是,ValueAnimator
並沒有將實時監聽的變化值設置到目標對象的指定屬性上,所以您必須手動設置。
ObjectAnimator
ValueAnimator
的子類,該類彌補了ValueAnimator
的不足,即它可以將回傳的值直接設置給對象的指定屬性( allows you to set a target object and object property to animate)
AnimatorSet
將若干動畫以一定的順序組合執行
估值器(Evaluators )負責將動畫執行的進度(百分比)換算成實際的屬性值,下表是各種類型的估值器:
IntEvaluator
屬性值以整數的形式變化
FloatEvaluator
屬性值以浮點數的形式變化
ArgbEvaluator
屬性值以十六進制數的形式變化,一般用於設置顏色屬性
TypeEvaluator
這是一個接口,用於定制 evaluator,如果需要改變的屬性值並不是int、float、color類型,那麼您必須實現該接口以指定如何計算屬性值。
時間插值器( time interpolator)指定了隨著時間的變化,屬性值的變化是以何種速率進行的。您可以自己實現TimeInterpolator
接口來定制屬性的變化速率。官方也定義一些interpolator ,如下表所示:
AccelerateDecelerateInterpolator
先加速再減速
@android:anim/accelerate_decelerate_interpolator
AccelerateInterpolator
加速運動
@android:anim/accelerate_interpolator
AnticipateInterpolator
先退一小步然後加速前進
@android:anim/anticipate_interpolator
AnticipateOvershootInterpolator
在上一個基礎上超出終點一小步再回到終點
@android:anim/anticipate_overshoot_interpolator
BounceInterpolator
最後階段彈球效果
@android:anim/bounce_interpolator
CycleInterpolator
周期運動
@android:anim/cycle_interpolator
DecelerateInterpolator
減速運動
@android:anim/decelerate_interpolator
LinearInterpolator
勻速運動
@android:anim/linear_interpolator
OvershootInterpolator
快速到達終點並超出一小步,最後回到終點
@android:anim/overshoot_interpolator
TimeInterpolator
一個接口,供您定制自己的Interpolator
/
創建ValueAnimator實例需要調用其工場方法,如ofInt()
、ofFloat()
或 ofObject()
,示例如下:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
示例中在1秒內將浮點數從0變為1,調用start()
方法執行動畫。
您也可以使用下面的方式執行動畫:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
在第一段代碼片段中,動畫並沒有作用在某個對象的屬性上,這需要為Animator
添加監聽器,實時獲取當前屬性值。
ObjectAnimator是ValueAnimator的子類,該子類可以直接將變化的值作用在對象的屬性上,示例如下:
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
靜態工場方法中的四個參數分別表示:作用的對象、對象中的指定屬性、起始值、終止值。為了使動畫生效,您必須注意以下幾點:
作用的屬性值必須有自己的setter()
方法(而且必須是駝峰命名法),比方說,若屬性名為alpha
,那麼必須包含setAlpha()
方法。若該setter()
方法不存在,那麼有如下三種可選方式:
在擁有權限的前提下,為該屬性添加setter()
方法;
在擁有權限的前提下,為對象添加一個包裝類,在包裝類中設置setter()
屬性值;
使用ValueAnimator替換;
如果您在調用ofFloat()
創建動畫時只傳入了一個屬性值(而不是既有啟始值和終止值),那麼系統將默認把這個值視為終止值,這就需要該屬性包含getter()
方法,以獲得其初始值,getter()
方法的命名規則與setter()
方法一致。
getter()方法獲取值的類型與setter()方法設置值的類型必須相同,如代碼為ObjectAnimator.ofFloat(targetObject, "propName", 1f)
的動畫聲明中, targetObject
必須包含targetObject.setPropName(float) 和float targetObject.getPropName()
方法。
實現動畫的效果可能需要調用invalidate()
方法強制view重繪,這需要在onAnimationUpdate()
的回調方法中進行。
在很多情況下,您需要將某個動畫在某個指定的時刻啟動,如在上一個動畫結束的時刻 等。android提供了AnimatorSet
類來組合若干動畫的播放順序,示例如下:
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
代碼片段的執行順序如下:
執行 bounceAnim
;
同時執行 squashAnim1
, squashAnim2
, stretchAnim1
, 和 stretchAnim2
執行 bounceBackAnim
.
執行 fadeAnim
.
如需獲取完整的demo示例,您可以點擊這個鏈接:Bouncing Balls
屬性動畫包含如下監聽器:
Animator.AnimatorListener
:
onAnimationStart()
:當動畫開始時回調;
onAnimationEnd()
:當動畫結束時回調;
onAnimationRepeat()
:當動畫重復執行時回調;
onAnimationCancel()
:當動畫取消時回調,這時也會回調onAnimationEnd()
。
ValueAnimator.AnimatorUpdateListener
:
onAnimationUpdate()
:動畫每執行一幀就回調一次(called on every frame of the animation)。該方法主要用於監聽屬性值的變化,然後將該屬性值設置到某個屬性上;
您也可以不實現Animator.AnimatorListener
接口,而是通過繼承AnimatorListenerAdapter
類的方式定制自己需要的監聽,如下所示:
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
//當動畫結束時,讓balls對象消失
balls.remove(((ObjectAnimator)animation).getTarget());
}
代碼實現了一個AnimatorListenerAdapter
,並重寫了onAnimationEnd()
,並實現自己的邏輯。
使用屬性動畫為ViewGroups設置布局動畫同樣方便,核心類是LayoutTransition
,它主要用於為ViewGroups中的View出現或消失時設置動畫,您可以調用LayoutTransition
的void setAnimator (int transitionType,
方法,其中參數2為您需要設置的動畫,而參數1為
Animator animator)LayoutTransition
常量值之一:
APPEARING
:某個view出現在viewgroup時的標志位;
CHANGE_APPEARING
:某個view替換其他view出現在viewgroup時的標志位;
DISAPPEARING
:某個view從viewgroup消失時的標志位;
CHANGE_DISAPPEARING
:某個view在viewgroup中被其他view替換時的標志位;
除此之外,還需要您在viewgroup的布局中添加動畫聲明,以告知系統其子view需要執行屬性動畫:
Keyframe
類用於在動畫執行到某一幀時,為該幀設置一個屬性值,這一幀也稱為關鍵幀。在PropertyValuesHolder
類中添加若干關鍵幀,系統會自動在兩個關鍵幀之間執行動畫。該類的初始化同樣使用靜態工廠方法ofInt(), ofFloat(), 或 ofObject()
,傳入的參數有兩個,第一個的范圍是0.0-1.0,表示動畫在時間軸上執行的百分比,第二個參數表示要對這個屬性賦值。示例如下:
//關鍵幀kf0 ,在動畫執行到0%時,屬性值為0f
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
//關鍵幀kf1 ,在動畫執行到50%時,屬性值為360f
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
//關鍵幀kf2 ,在動畫執行到100%時,屬性值為0f
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
//初始化PropertyValuesHolder ,依次傳入作用的屬性"rotation"、以及三個關鍵幀
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
//調用ObjectAnimator的靜態方法ofPropertyValuesHolder初始化ObjectAnimator,傳入的第1個參數target為屬性"rotation"所屬對象,第二個參數為PropertyValuesHolder
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
//設置動畫的執行時間
rotationAnim.setDuration(5000ms);
上述代碼在執行時,系統會根據kf0,kf1,kf2
這三個關鍵幀的值把動畫連起來,默認為勻速執行,當然,您也可以添加插值器定制執行的速率。
您可以將屬性動畫作用在View的這些屬性上:
translationX
、translationY
:表示view在橫向或縱向上移動的距離;
rotation、 rotationX 、 rotationY
:前者表示view在2D平面上旋轉的角度,後兩者表示view在3D空間中旋轉的角度;
scaleX 、 scaleY
:表示view的縮放;
pivotX 、 pivotY
:表示view在縮放或旋轉的中心點,若不設置,默認為view的中心點;
x 、 y
:分別表示當前view的左邊距其父容器左邊的距離、當前view的上邊距其父容器上邊的距離;
alpha
:表示view的透明度,取值為0.0-1.0,0表示完全透明,1表示完全不透明;
示例如下:
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
Android提供了便捷的ViewPropertyAnimator
類,它可以將若干動畫同時執行,而僅僅需要調用一句代碼,下面的三段代碼分別創建了2個ObjectAnimator
對象、1個ObjectAnimator
對象、0個個ObjectAnimator
對象,卻實現了相同的效果,顯然最後一種方式最簡便,它就是運用了ViewPropertyAnimator
:
ObjectAnimator
對象:
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
1個ObjectAnimator
對象:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
0個ObjectAnimator
對象:
myView.animate().x(50f).y(100f);
為了讓系統識別您定義的XML文件是屬性動畫,需要將該文件放在res/animator/
目錄下。
XML標簽與Java代碼的對應類如下:
ValueAnimator
-
ObjectAnimator
-
AnimatorSet
-
下面演示了一個屬性動畫的集合,在集合中嵌套了一個集合和一個單獨的動畫:
在Java代碼中執行該動畫:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
下面簡要介紹一下補間動畫。補間動畫只能作用在View上,您同樣可以使Java代碼或XML的形式編寫動畫,官方推薦使用XML的形式,為了讓系統識別您定義的XML文件為補間動畫,需要將文件定義在res/anim/
目錄下,示例如下:
其中屬性android:fillAfter
表示是在動畫結束時將其停留在最後一幀,android:startOffset
表示延遲執行的毫秒數。
在Java代碼中執行XML動畫:
ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);
幀動畫是一系列圖片按照一定的順序展示的過程,和放電影的機制想死,它的原理實在一定時間內切換多張有細微差異的圖片而達到的動畫效果。幀動畫同樣可以被定義在XML文件中或由Java代碼實現,若定義在XML中,需要將其放在res/drawable/
目錄下,其中 標簽必須作為根元素,
標簽代表一幀動畫,示例如下:
android:oneshot="true">
-
-
-
在Java代碼中執行XML動畫:
AnimationDrawable rocketAnimation;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
rocketAnimation.start();
return true;
}
return super.onTouchEvent(event);
}
需要注意的是,start()
方法不可以在Activity的onCreate()
回調中執行,因為這時AnimationDrawable
還未完全依附於window(the AnimationDrawable is not yet fully attached to the window);若您需要執行一個無交互並且可以立即執行的動畫,那麼可以在Activity的回調方法onWindowFocusChanged()
中執行該動畫。
Android 5.0 Lollipop 是迄今為止最重大的一次發布,因為 material design 的存在,android的界面風格發生了新的改變,這是一門新的設
1 背景在Android中任何耗時的操作都不能放在UI主線程中,所以耗時的操作都需要使用異步實現。同樣的,在ContentProvider中也可能存在耗時操作,這時也該使
如何使用ES文件浏覽器的雲功能。ES文件浏覽器是android手機上非常常見的文件浏覽器,在新的版本中還加入了網絡的功能,還可以通過ES文件浏覽器可以直接訪
當setCanceledOnTouchOutside(true),點擊陰影處,dialog dismiss時鍵盤不消失的問題。如圖一開始覺得很簡單,監聽下onDimiss