android動畫分為三種 View動畫 幀動畫 屬性動畫 其中幀動畫也屬于View動畫 不過和常見的View旋轉、平移、縮放、透明度的表現形式不同
7.1.1View動畫的種類
使用View動畫首先需要建立xml檔案這個檔案的路徑為res/anim/filename.xml ,描述檔案有固定的文法
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="true">
<alpha
android:fromAlpha="10.0"
android:toAlpha="10.0" />
<scale
android:fromXScale="10dp"
android:fromYScale="10dp"
android:pivotX="10"
android:pivotY="10"
android:toXScale="10dp"
android:toYScale="10dp" />
<translate
android:fromXDelta="10"
android:fromYDelta="10"
android:toXDelta="10"
android:toYDelta="10" />
<rotate
android:fromDegrees="10"
android:pivotX="10"
android:pivotY="10"
android:toDegrees="10" />
</set>
從上邊的文法來看,View的動畫既可以是單個動畫也可以由一系列的動畫組成
<set>标簽表示動畫集合,對應AnimationSet類它包含很多個類有兩個屬性:
android:interpolator :表示動畫集合所使用的內插補點器,內插補點器影響動畫的速度,比如非勻速動畫就需要內插補點器來制作動畫的過程,這個屬性可以不指定,預設為加速減速內插補點器
android:shareInterpolator:表示集合中的動畫是否合計和使用一個內插補點器,如果集合不指定內插補點器,那麼子動畫就需要單獨的指定所需要的內插補點器了
< translate>标簽表示平移動畫:
- android:fromXDelta
表示x的起始值,比如0
- android:fromYDelta
表示y的結束值,比如100
- android:toXDelta
表示y的起始值
- android:toYDelta
表示y的結束值
< scale>标簽表示的是縮放動畫
android:fromXScale
水準方向縮放的起始值,比如0.5
android:fromYScale
豎直方向縮放的起始值
android:pivotX
縮放軸點的x坐标,它會影響縮放的效果
android:pivotY
縮放軸點的y坐标,它會影響縮放的效果
android:toXScale
水準方向縮放的結束值,比如1.2
android:toYScale
豎直方向縮放的起始值
<routate>表示動畫的旋轉,對應RotateAnimation,它可以使View具有旋轉的動畫效果
android:fromDegrees
旋轉開始的角度,比如0
android:toDegrees
旋轉結束的角度,比如180
android:pivotX
旋轉軸點的x
android:pivotY
旋轉軸點的y
在旋轉中也有軸的概念,他也會影響到旋轉的效果,軸點扮演者旋轉軸的角色,view圍繞着軸點進行旋轉,預設情況下在view的中心,考慮到一種情況,view圍繞自己的中心,和圍繞左上角進行90度顯然是不同的軌迹
< alpha>表示透明動畫。對應的AlphaAnimation
- android:fromAlpha
表示透明度的起始值,比如0.1
- android:toAlpha
上面都隻是很簡單的介紹了XM格式,具體的使用方法還是看文檔,我們還有一些常用的屬性如下
- android:duration
動畫的時間
- android:fillAfter
動畫結束之後是否停留在結束的位置
除了在xml中建立動畫 還可以通過代碼實作 但是對于View動畫來說建議采用XML來定義動畫,這是因為XML的可讀性更好,淡然在實際開發中使用xml也是第一選擇
7.1.2自定義View動畫
除了系統提供的四種View動畫外,我們還可以自定義View動畫自定義動畫,在實際開發過程中基本上用不到
7.1.3幀動畫
幀動畫就是順序播放的一組圖檔,系統提供了一個AnimationDrawable類來實作幀動畫
在xml中定義
7.2View動畫的使用場景
view動畫還可以在一些特殊的場景使用 比如在ViewGroup中可以控制子元素的出場效果,實作不同activity之間的切換效果
7.2.1 LayoutAnimation 作用于ViewGroup為ViewGroup制定一個動畫這樣他的子元素出場的時候就具有這種動畫了,這種效果常被用于listview中
7.2.2 Activity的切換效果
啟動一個
startActivity(new Intent(MainActivity.this,OneActivity.class));
//這是activity的跳轉動畫
overridePendingTransition(R.anim.animation,R.anim.anim_layout);
退出一個
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(R.anim.animation, R.anim.anim_layout);
}
7.3屬性動畫
屬性動畫可以對任何隊形做動畫們甚至沒有對象也可以,屬性動畫效果得到加強,不再像View動畫支援4種屬性動畫有ValueAnimator,ObjectAnimator,AnimatorSet; ObjectAnimator繼承自ValueAnimator,而AnimatorSet可以定義一組動畫一般我們都用ObjectAnimator來聲明一個ValueAnimator在ObjectAnimator聲明屬性變化值。
屬性動畫可以對任意對象的屬性進行動畫,而不僅僅是View 動畫的預設時間是300 預設幀率是10
7.3.1使用屬性動畫
(1)改變一個對象的translationY屬性,讓其沿着Y軸向上平移一個時間,該動畫在預設的時間完成,
ObjectAnimator.ofFloat(iv_icon, "translationY", -iv_icon.getHeight()).start();
(2)改變一個對象的背景顔色值,典型的就是改變view的背景,下面的動畫是讓view的背景從0xffff8080到0xff8080ff,動畫會無限循環和反轉
ValueAnimator valueAnimator =
ObjectAnimator.ofInt(ll_content, "backgroundColor", 0xFFFF8080, 0xFF8080FF);
valueAnimator.setDuration(3000);
valueAnimator.setEvaluator(new ArgbEvaluator());
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.start();
(3)動畫集合,5s内對view的旋轉,平移,縮放和透明度進行改變
實作組合動畫功能主要需要借助AnimatorSet這個類,這個類提供了一個play()方法,如果我們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)将會傳回一個AnimatorSet.Builder的執行個體,AnimatorSet.Builder中包括以下四個方法:
- after(Animator anim) 将現有動畫插入到傳入的動畫之後執行
- after(long delay) 将現有動畫延遲指定毫秒後執行
- before(Animator anim) 将現有動畫插入到傳入的動畫之前執行
- with(Animator anim) 将現有動畫和傳入的動畫同時執行
好的,有了這四個方法,我們就可以完成組合動畫的邏輯了,那麼比如說我們想要讓TextView先從螢幕外移動進螢幕,然後開始旋轉360度,旋轉的同時進行淡入淡出操作,就可以這樣寫:
1 2 3 4 5 6 7 | |
可以看到,這裡我們先是把三個動畫的對象全部建立出來,然後new出一個AnimatorSet對象之後将這三個動畫對象進行播放排序,讓旋轉和淡入淡出動畫同時進行,并把它們插入到了平移動畫的後面,最後是設定動畫時長以及啟動動畫。
當然也可以直接使用playTogether方法将所有的動畫操作放入一個集合中
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(iv_icon, "rotationX", 0, 360), 中心x軸進行3d旋轉
ObjectAnimator.ofFloat(iv_icon, "rotationY", 0, 180),中心y軸進行3d旋轉
ObjectAnimator.ofFloat(iv_icon, "rotation", 0, -90),中心進行平面旋轉
ObjectAnimator.ofFloat(iv_icon, "translationX", 0, 90),x軸進行平移
ObjectAnimator.ofFloat(iv_icon, "translationY", 0, 90),y軸進行平移
ObjectAnimator.ofFloat(iv_icon, "scaleX", 0, 1.5f),,x軸進行縮放
ObjectAnimator.ofFloat(iv_icon, "scaleY", 0, 0.5f),y軸進行縮放
ObjectAnimator.ofFloat(iv_icon, "alpha", 0, 2.5f, 1) 透明度變化
);
set.setDuration(3000).start();
屬性動畫的各個參數是比較好了解的,我們簡單來說下他們之間的含義
propertyName:表示屬性動畫作用對象的屬性的名稱
duration:表示動畫的時長
valueFrom:表示屬性的起始值
valueTo:表示屬性的結束值
startOffset:表示動畫的延遲時間,當動畫開始後,需要延遲多少毫秒才會真正的播放
repeatCount:表示動畫的重複次數
repeatMode:表示動畫的重複模式
valueType:表示propertyName有兩個屬性有int和float兩個可選項,分别表示屬性的類型,和浮點型,另外,如果所制定的是顔色類型,那麼就不需要指定propertyName,系統會自動對顔色類型進行處理
實際開發中一般使用代碼動态建立屬性動畫
ValueAnimator、 ObjectAnimator、 AnimatorSet都可以使用addListener添加監聽事件,生成四個方法,如果你覺得每次實作四種方法,太磨叽,那麼AnimatorListenerAdapter也可以實作你的邏輯,他可以實作你想實作的某一個方法。
7.3.4 對任意的屬性做動畫
這裡最一個需求,就是給buttion設定一個動畫,讓他的寬度從目前的變成500px,這個可以用view動畫來搞定,但是你仔細想想,view不能對寬高變化,所有我麼可以使用屬性動畫,
我們會發現button确實拉伸了
但是書中卻說不能改變
看下大佬的分享
下面對屬性動畫的原理:屬性動畫要求動畫作用在對象提供的get/set方法,屬性動畫根據外界傳遞的該屬性的初始值和最終值,以動畫效果多次去set,每次傳遞的set方法的值都不一樣,确切來說是随着時間的時間推移,所傳遞的值越來越接近最終值,總結一下,我們對object的屬性abc做動畫,如果想讓動畫生效,要同時滿足兩個條件:
(1)object必須要提供set方法,如果動畫的時候沒有傳遞初始值,那麼我們還要提供get方法,因為系統要去取abc的屬性(如果這條不滿意,程式直接Crash)
(2)object的set方法對abc所做的改變必須通過某種方法反應,比如帶來UI的改變(如果這條不滿足,動畫無效果但是不會Crash)
以上條件缺一不可,那麼為什麼我們對button的width屬性做動畫沒有效果,這是因為button内部雖然提供了get/set方法,但是這個set方法并不是改變視圖大小,他是textview新添加的方法,view是沒有這個setWidth方法的,由于button繼承了textview,所有button也就有了set方法,下面看一下這個get/get的源碼
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
從上述的源碼可以看出,get的确是擷取view的寬度,而set是textview的專屬方法,他的作用不是設定view的寬度,而是設定textview的最大寬度和最小寬度,這個和textview的寬度不死一個東西,具體來說,textview的寬度對應XML中的android:layout_width,而textview還有一個屬性android:width,這個就對應了setwidth,總之textview和button的set/get幹的不是同一件事,通過set無法改變控件的寬度,是以對width做屬性動畫沒有效果,對于屬性動畫的兩個條件來說,本例中的動畫隻滿足了第一個條件,我們有三個解決辦法:
給你的對象增加set/get方法,前提是你有權限的話
用這個類來包裝原始對象,間接提供get/set方法
采用ValueAnimator,監聽動畫過程自己去實作
我們來具體的實作下這三個解決辦法
1.給你的對象增加set/get方法,前提是你有權限的話
這個的意思很好了解,如果你有權限的話,加個set/get方法就搞定了,但是很多時候我們沒有權限去這麼做,比如本文開頭所提到的問題,你無法給button加上一個合乎要求的setwidth方法,因為這個是Android SDK内部實作的,這個方法很簡單,但是往往是不可行的,這裡就不對其進行更多的分析了
2.用這個類來包裝原始對象,間接提供get/set方法
private void performAnimate() {
ViewWrapper viewWrapper = new ViewWrapper(btn);
ObjectAnimator.ofInt(viewWrapper, "width", 500).setDuration(1000).start();
}
private static class ViewWrapper {
private View mTarget;
public ViewWrapper(View mTarget) {
this.mTarget = mTarget;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
上述代碼在1000ms中寬度增加到500,為了達到這個效果我們寫了一個包裝類去提供方法,這樣也就完美的實作了
3.采用ValueAnimator,監聽動畫過程自己去實作
首先說下什麼是ValueAnimator,ValueAnimator本身不作用于任何對象,也就是說直接使用它沒有任何的效果,他可以對一個值做動畫,然後我們監聽這個過程,在過程中修改我們對象的屬性值,這樣就相當于我們的對象做了動畫,下面我們用例子來說明
private void performAnimator(final View target, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//持有一個IntEvaluator對象,友善下面估值的時候使用
private IntEvaluator mEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//獲得目前動畫的進度值,整形1-100之間
int currentValue = (int) animation.getAnimatedValue();
//獲得目前進度占整個動畫之間的比例,浮點0-1之間
float fraction = animation.getAnimatedFraction();
//直接使用整形估值器,通過比例計算寬度,然後再設定給按鈕
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
上面的代碼的效果和剛才的viewwrapper是一樣的,關于ValueAnimator還要再說一下,拿上來的例子來說,他會在5s内将一個數1變成100,然後動畫的每一幀會回調的每一幀onAnimationUpdate方法,在這個方法裡,我們可以擷取目前的值和占用的比例我們可以計算出寬度是多少,比如時間過去了一半,目前值是50,比例是0.5,假設起始值為100,最終是500px,那麼500-100=400,所有這個時候乘以0.5=200,這些都是内部實作,我們不用自己寫,直接用。
7.3.5 屬性動畫的工作原理