其實動畫這個東西我已經瞭解過很長一段時間了,可是一直沒系統的整理過。關於android中的各類動畫雖然都會用,但總怕本身會慢慢遺忘。這回看了幾篇動畫分析的文章,本身也學到了一些東西,在此就梳理一下。html
參考博文以下,感謝大神們的分享:java
http://www.open-open.com/lib/view/open1329994048671.htmlandroid
http://www.tuicool.com/articles/yeM3my算法
http://blog.csdn.net/singwhatiwanna/article/details/17841165
編程
http://blog.csdn.net/lmj623565791/article/details/38067475api
http://blog.csdn.net/lmj623565791/article/details/38092093app
注意:全部view的動畫就是被限制在它的父控件中的,即便你作了view的移動,它也不可能顯示在父控件的外邊。也就是說父控件是一個舞臺,演員能夠在舞臺上處處走動,但若是超過了舞臺,那麼觀衆就看不到了。ide
1、View Animation(Tween Animation)函數
View Animation(Tween Animation):也可稱爲補間動畫(Tween Animation),給出兩個關鍵幀,經過一些算法將給定屬性值在給定的時間內在兩個關鍵幀間漸變。工具
View animation只能應用於View對象,並且只支持一部分屬性,如支持縮放旋轉而不支持背景顏色的改變。
對於View animation,它只是改變了View對象繪製的位置,注意這是「繪製」,而不是實際的位置。好比你讓一個button變成它的兩倍大小,可是它能接受你點擊的區域仍是原來的button區域,沒有作任何改變。
View Animation支持設定多種動畫樣式,也能夠設定這些動畫的執行順序,支持經過xml和代碼兩種方式來設置動畫效果。
動畫舉例
【 By XML 】
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true" android:startOffset="50"> <alpha android:duration="200" android:fromAlpha="1.0" android:toAlpha="0.0" /> </set>
Animation aimation = AnimationUtils.loadAnimation(this, R.anim.anim); final ImageView imageView = (ImageView) findViewById(R.id.imageView_id); imageView.startAnimation(aimation);
能夠對動畫添加監聽器
aimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { animation = null; } @Override public void onAnimationRepeat(Animation animation) { } });
若是你是用AnimationSet設置動畫的話,animationSet也是繼承自Animation,因此也有setAnimationListener的方法
詳細代碼能夠參考:http://www.cnblogs.com/tianzhijiexian/p/3983616.html
【 By JAVA 】
經過java代碼來作的,能夠參考這篇文章。其實也就是各類類繼承Animation,而後有本身獨特的方法,也能夠經過AnimationSet進行各類設置。
http://www.cnblogs.com/tianzhijiexian/p/3981241.html
Drawable Animation(Frame Animation):幀動畫,就像GIF圖片,經過一系列Drawable依次顯示來模擬動畫的效果。在XML中的定義方式以下:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
<item android:drawable="@drawable/rocket_thrust1" android:duration="200" /> <item android:drawable="@drawable/rocket_thrust2" android:duration="200" /> <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>
必須以<animation-list>爲根元素,以<item>表示要輪換顯示的圖片,duration屬性表示各項顯示的時間。XML文件要放在/res/drawable/目錄下。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main); imageView = (ImageView) findViewById(R.id.imageView1); imageView.setBackgroundResource(R.drawable.drawable_anim); AnimationDrawable anim = (AnimationDrawable) imageView.getBackground(); } public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { anim.stop(); anim.start(); return true; } return super.onTouchEvent(event); }
我在實驗中遇到兩點問題:
3、Property Animation
屬性動畫,這個是在Android 3.0中才引進的,它更改的是對象的實際屬性,如Button的縮放,Button的位置與大小屬性值都改變了。並且Property Animation不止能夠應用於View,還能夠應用於任何對象(Object)。Property Animation只是表示一個值在一段時間內的改變,當值改變時要作什麼事情徹底是你本身決定的。
在Property Animation中,能夠對動畫應用如下屬性:
4、ValueAnimator
ValueAnimator包含Property Animation動畫的全部核心功能,如動畫時間,開始、結束屬性值,相應時間屬性值計算方法等。它其實就是一個計算器,並不能實際執行動畫效果。它能夠計算出動畫要執行的時間,每隔幾毫秒刷新一次等等,但具體如何執行動畫,它是無論的。你須要在ValueAnimator的ValueAnimator.onUpdateListener監聽器中進行設置。
這裏給ValueAnimator對象設置了一個值,由於是一個值,因此會默認爲是最終的結果值,動畫默認從當前的值到你設定的這個值。若是你設定兩個值,那麼它就意味着是從第一個值到另外一個值進行動畫。
// set one value,it is final value. // If you set two values in it.It means animation will start from first value to Second value. ValueAnimator animator = ValueAnimator.ofFloat(1f); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { } });
animator.start();
這個AnimationUpdateListener將會在動畫執行過程當中觸發。若是你沒在這裏作任何處理,那麼即便是執行了(start())也不會有任何動畫的。
實際運用——自由落體 & 拋物線
若是我但願小球拋物線運動【實現拋物線的效果,水平方向100px/s,垂直方向加速度200px/s*s 】,分析一下,貌似只和時間有關係,可是根據時間的變化,橫向和縱向的移動速率是不一樣的,咱們該咋實現呢?此時就要重寫TypeValue的時候了,由於咱們在時間變化的同時,須要返回給對象兩個值,x當前位置,y當前位置。
/** * 拋物線 * @param view */ public void paowuxian(View view) { ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.setDuration(3000); valueAnimator.setObjectValues(new PointF(0, 0)); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setEvaluator(new TypeEvaluator<PointF>() { // fraction = t / duration @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { Log.e(TAG, fraction * 3 + ""); // x方向200px/s ,則y方向0.5 * 10 * t PointF point = new PointF(); point.x = 200 * fraction * 3; point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3); return point; } }); valueAnimator.start(); valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { PointF point = (PointF) animation.getAnimatedValue(); mBlueBall.setX(point.x); mBlueBall.setY(point.y); } }); }
能夠看到,由於ofInt,ofFloat等沒法使用,咱們自定義了一個TypeValue,每次根據當前時間返回一個PointF對象,(PointF和Point的區別就是x,y的單位一個是float,一個是int;RectF,Rect也是)PointF中包含了x、y的當前位置,而後咱們在監聽器中獲取,動態設置屬性。
5、ObjectAnimator
實際應用中通常都會用ObjectAnimator來產生某一對象的動畫,但用ObjectAnimator有必定的限制,要想使用ObjectAnimator,應該知足如下條件:
若是上述條件不知足,則不能用ObjectAnimator,應用ValueAnimator代替。
下面是將imageview進行透明度漸變的例子。
ObjectAnimator oa=ObjectAnimator.ofFloat(imageview, "alpha", 0f, 1f); oa.setDuration(3000); oa.start();
根據應用動畫的對象或屬性的不一樣,可能須要在onAnimationUpdate函數中調用invalidate()函數刷新視圖。
屬性動畫要求動畫做用的對象提供該屬性的get和set方法,屬性動畫根據你傳遞的該熟悉的初始值和最終值,以動畫的效果屢次去調用set方法,每次傳遞給set方法的值都不同,確切來講是隨着時間的推移,所傳遞的值愈來愈接近最終值。總結一下,你對object的屬性xxx作動畫,若是想讓動畫生效,要同時知足兩個條件:
以上條件缺一不可
那麼若是咱們的object沒辦法知足這個條件呢?好比button的setWidth方法僅僅是設置它的最小寬度(minWidth),對於實際寬度不會產生任何影響。若是讓它實際改變就須要用
btn.getLayoutParams().width = xxx;
來設置,但這種經過方法來得到public值的方法又不適合ObjecAnimator的應用場景,改怎麼辦呢?
針對上述問題,Google告訴咱們有3中解決方法:
1. 給你的對象加上get和set方法,若是你有權限的話
對於View咱們沒權限給其添加各類屬性,因此這種方法僅僅適合於本身定義的Object對象
2. 用一個類來包裝原始對象,間接爲其提供get和set方法
這個方法很好,你能夠寫一個類繼承那個Object對象,在子類中添加各類set、get方法,更好的辦法是寫一個包裝類,傳入一個object對象,而後給其添加各類方法。
private void performAnimate() { ViewWrapper wrapper = new ViewWrapper(mButton); ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start(); } @Override public void onClick(View v) { if (v == mButton) { performAnimate(); } } private static class ViewWrapper { private View mTarget; public ViewWrapper(View target) { mTarget = target; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width) { mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } }
上面類中ViewWrapper這個內部類就是一個包裝類,它的構造函數傳入一個View對象,而後提供了set和get方法,這樣咱們就能夠對它進行動畫的操做了
3. 採用ValueAnimator,監聽動畫過程,本身實現屬性的改變
這個方法就有些彆扭了,但擴展性是最強的。ValueAnimator咱們在上面已經介紹過了,下面是在動畫執行過程當中能的獲得的東西。
ValueAnimator animator = ValueAnimator.ofFloat(1, 1); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Integer currentValue = (Integer)animation.getAnimatedValue(); // current Value(0~100) float fraction = valueAnimator.getAnimatedFraction(); // progress of animation } });
getAnimatedFraction() 這個能夠獲得當前動畫的進度,獲得的是浮點型數據頗有用。api12中加入的,感受比getAnimationValue有用
getAnimatedValue(String propertyName) 獲得某個特定屬性當前的值,這個可用性就很高了。若是有特殊要求能夠用它
其他的屬性都在下面了,看名字就知道是什麼意思啦~
實際運用
這個例子是將一個view進行移動和拉伸的動畫。涉及了多個屬性,當你要讓view選裝或者是移動的時候,請務必設定PivotX,PivotY來指定座標,若是不設定的話,移動和選擇默認的中心點都是Object的中心
public void startViewSimpleAnim(final View fromView,Rect finalBounds,int startOffsetY,int finalOffsetY, float startAlpha, float finalAlpha) { Rect startBounds = new Rect(); startBounds.set(Position.getGlobalVisibleRect(fromView)); //設置偏移量 startBounds.offset(0, -startOffsetY); finalBounds.offset(0, -finalOffsetY); //設定拉伸或者旋轉動畫的中心位置,這裏是相對於自身左上角 fromView.setPivotX(0f); fromView.setPivotY(0f); //計算拉伸比例 float scaleX = (float)finalBounds.width() / startBounds.width(); float scaleY = (float)finalBounds.height() / startBounds.height(); AnimatorSet set = new AnimatorSet(); ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(fromView, "alpha", startAlpha, finalAlpha); ObjectAnimator xAnim = ObjectAnimator.ofFloat(fromView, "x", startBounds.left, finalBounds.left); ObjectAnimator yAnim = ObjectAnimator.ofFloat(fromView, "y", startBounds.top, finalBounds.top); ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(fromView, View.SCALE_X, 1f, scaleX); ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(fromView, View.SCALE_Y,1f, scaleY); set.play(alphaAnim).with(xAnim).with(yAnim).with(scaleXAnim).with(scaleYAnim); set.setStartDelay(mStartDelay); set.setDuration(mAnimTime); set.setInterpolator(mInterpolator); set.addListener(new AnimListener(fromView,null)); set.start(); }
若是你操做對象的該屬性方法裏面,好比上例的setRotationX若是內部沒有調用view的重繪,則你須要本身按照下面方式手動調用。
anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // view.postInvalidate(); // view.invalidate(); } });
用xml文件來建立屬性動畫
你們確定都清楚,View Animator 、Drawable Animator均可以在anim文件夾下建立動畫,而後在程序中使用,甚至在Theme中設置爲屬性值。固然了,屬性動畫其實也能夠在文件中聲明。
首先在res下創建animator文件夾,而後創建res/animator/scalex.xml
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="2.0" android:valueType="floatType" > </objectAnimator>
而後經過動畫工具類就能夠加載xml中的動畫文件了
AnimatorInflater.loadAnimator(this, R.anim.anim);
public void scaleX(View view) { // 加載動畫 Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scalex); anim.setTarget(mMv); anim.start(); }
縱向與橫向同時縮放
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together" > <objectAnimator android:duration="1000" android:propertyName="scaleX" android:valueFrom="1" android:valueTo="0.5" > </objectAnimator> <objectAnimator android:duration="1000" android:propertyName="scaleY" android:valueFrom="1" android:valueTo="0.5" > </objectAnimator> </set>
使用set標籤,有一個orderring屬性設置爲together,【還有另外一個值:sequentially(表示一個接一個執行)】
6、經過AnimatorSet來控制多個動畫
AnimatorSet和AnimationSet相似,能夠操做多個動畫屬性。AnimationSet提供了一個把多個動畫組合成一個組合的機制,並可設置組中動畫的時序關係,如同時播放,順序播放等。
例子01:
AnimatorSet bouncer = new AnimatorSet(); bouncer.play(anim1).before(anim2); bouncer.play(anim2).with(anim3); bouncer.play(anim2).with(anim4) bouncer.play(anim5).after(amin2); animatorSet.start();
例子02:
animSet.playTogether(anim1, anim2);
animSet.start();
使用playTogether兩個動畫同時執行,固然還有playSequentially依次執行
例子03:
/** * anim1,anim2,anim3同時執行 * anim4接着執行 */ AnimatorSet animSet = new AnimatorSet(); animSet.play(anim1).with(anim2); animSet.play(anim2).with(anim3); animSet.play(anim4).after(anim3); animSet.setDuration(1000); animSet.start();
若是咱們有一堆動畫,如何使用代碼控制順序,好比1,2同時;3在2後面;4在1以前
注意:animSet.play().with();也是支持鏈式編程的,可是不要想太多。
好比:animSet.play(anim1).with(anim2).before(anim3).before(anim5);
這樣是不行的,系統不會根據你寫的這一長串來決定前後的順序,因此麻煩你按照上面例子的寫法,多寫幾行
7、ViewPropertyAnimator
若是須要對一個View的多個屬性進行動畫能夠用ViewPropertyAnimator類,該類對多屬性動畫進行了優化,會合並一些invalidate()來減小刷新視圖,該類在3.1中引入。
例子01:
如下兩段代碼實現一樣的效果
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
myView.animate().x(50f).y(100f);
例子02:
在SDK12的時候,給View添加了animate方法,更加方便的實現動畫效果。
btn.animate().x(200).y(200).alpha(0f); 就能夠實現動畫效果了~
例子03:
簡單的使用mBlueBall.animate().alpha(0).y(mScreenHeight / 2).setDuration(1000).start()就能實現動畫~~不過須要SDK11,此後在SDK12,SDK16又分別添加了withStartAction和withEndAction用於在動畫前,和動畫後執行一些操做。固然也能夠.setListener(listener)等操做。
package com.example.zhy_property_animation; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.app.Activity; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.widget.ImageView; public class ViewAnimateActivity extends Activity { protected static final String TAG = "ViewAnimateActivity"; private ImageView mBlueBall; private float mScreenHeight; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.view_animator); DisplayMetrics outMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(outMetrics); mScreenHeight = outMetrics.heightPixels; mBlueBall = (ImageView) findViewById(R.id.id_ball); } public void viewAnim(View view) { // need API12 mBlueBall.animate()// .alpha(0)// .y(mScreenHeight / 2).setDuration(1000) // need API 12 .withStartAction(new Runnable() { @Override public void run() { Log.e(TAG, "START"); } // need API 16 }).withEndAction(new Runnable() { @Override public void run() { Log.e(TAG, "END"); runOnUiThread(new Runnable() { @Override public void run() { mBlueBall.setY(0); mBlueBall.setAlpha(1.0f); } }); } }).start(); } }
8、TypeEvalutors<T>
根據屬性的開始、結束值與TimeInterpolation計算出的因子計算出當前時間的屬性值,Android提供瞭如下幾個evalutor:
自定義TypeEvalutor很簡單,只須要實現一個方法,如FloatEvalutor的定義:
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } }
根據動畫執行的時間跟應用的Interplator,會計算出一個0~1之間的因子,即evalute函數中的fraction參數,經過上述FloatEvaluator應該很好看出其意思。
9、當Layout改變時應用動畫
ViewGroup中的子元素能夠經過setVisibility使其Visible、Invisible或Gone,當有子元素可見性改變時,能夠向其應用動畫。
經過LayoutTransition類的常量(第一個參數)能夠區分動畫的類型,第二個參數爲Animator。
mTransition.setAnimator(LayoutTransition.DISAPPEARING, customDisappearingAnim);
更多的解釋
詳細的例子,請參考:http://blog.csdn.net/lmj623565791/article/details/38092093
順便說下:Android 3.0已經原生支持了Animating Layout,當Layout變化的時候,系統會根據Layout變化先後,自動顯示動畫。
只要給layout添加:android:animateLayoutChanges="true"便可,並且動畫還能夠經過setLayoutTransition()本身定義。
參考博文以下:
http://www.open-open.com/lib/view/open1329994048671.html
http://www.tuicool.com/articles/yeM3my
http://blog.csdn.net/singwhatiwanna/article/details/17841165