Android中,動畫可以分為三種,分別是補間動畫、幀動畫以及屬性動畫,接下來將對這三種動畫的使用做一個詳細的介紹。
一、補間動畫
補間動畫也稱View動畫,所以它的作用對象只能是View,它有四種典型的變換效果,分別是:平移動畫、縮放動畫、旋轉動畫、透明度動畫。這四種動畫效果可以通過java代碼的方式動態的創建,也可以通過xml文件來創建。
1. 首先看一下通過java代碼的創建方式:
1.1 平移動畫
平移動畫是通過TranslateAnimation類來實現的,常用的構造函數有以下兩個:
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) {
}
先說第一個構造函數,參數fromXDelta、toXDelta代表x方向平移的起始值和結束值,單位為像素,若toXDelta減fromXDelta大于0,則View右移,否則左移。fromYDelta、toYDelta是同樣的道理,差值大于0View下移,否則上移。
要實現一個View下移100像素可以這么做:
TranslateAnimation translateAnimation = new TranslateAnimation(0f, 0f, 0f, 100f);
translateAnimation.setDuration(2000);//動畫的持續時間,單位毫秒
translateAnimation.setFillAfter(true);//參數為true表示動畫結束后View停留在結束為止
view.startAnimation(translateAnimation);//開始動畫
再看第二個構造函數,在x方向上,fromXType、toXType有三種類型:Animation.ABSOLUTE、Animation.RELATIVE_TO_SELF、Animation.RELATIVE_TO_PARENT,分別代表絕對像素、相對于自身平移、相對于父View平移。fromXValue、toXValue,當type為Animation.ABSOLUTE時,這個兩個值為具體的像素值,當type為Animation.RELATIVE_TO_SELF或Animation.RELATIVE_TO_PARENT,這個兩個值為比例值,取值范圍是[0f, 1.0f], y方向上同理。
具體的用法如下:
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, -1.0f);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(true);
view.startAnimation(translateAnimation);
此時type都是Animation.RELATIVE_TO_SELF,toYValue的值是-1.0f,即100%,此時則view向上平移自身高度的距離,即就是常見的隱藏title的效果。當type為Animation.RELATIVE_TO_PARENT時,則view向上平移父view高度距離。
1.2 縮放動畫
縮放動畫是通過ScaleAnimation類實現的,常用構造函數如下:
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {
}
x方向上,參數fromX、toX分別代表view在水平方向縮放的起始比例和結束比例,都是大于等于0的浮點數。pivotXType代表縮放類型,有三種Animation.ABSOLUTE,、Animation.RELATIVE_TO_SELF、Animation.RELATIVE_TO_PARENT,pivotXValue代表縮放的中心點,可以是具體的像素值,可以是比比例值,比例值范圍是[0f, 1.0f],比例值是常用的,y方向上同理。
具體用法如下:
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 2f, 1.0f, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(2000);
scaleAnimation.setFillAfter(true);
view.startAnimation(scaleAnimation);
實現了view相對于自身中心,在x方向拉伸為原來2倍,在y方向縮小為原來0.5倍。
1.3 旋轉動畫
旋轉動畫是通過RotateAnimation實現的,常用構造函數如下:
public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue) {
}
參數fromDegrees、toDegrees代表旋轉的開始角度和結束角度,pivotXValue、pivotYValue代表旋轉的中心位置,可以是絕對的像素值,也可以是比例值,比例值范圍是[0f, 1.0f],pivotXType、pivotYType代表旋轉類型,有三種Animation.ABSOLUTE,、Animation.RELATIVE_TO_SELF、Animation.RELATIVE_TO_PARENT
具體用法如下:
RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
rotateAnimation.setFillAfter(true);
view.startAnimation(rotateAnimation);
實現了view相對自身中心,瞬時間旋轉360度。
1.4 透明度動畫
透明度動畫通過AlphaAnimation類實現,構造函數如下:
public AlphaAnimation(float fromAlpha, float toAlpha) {
}
參數fromAlpha、toAlpha代表透明度的起始值和結束值,0f代表完全透明,1.0f則無透明度。
具體用法如下:
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
alphaAnimation.setDuration(2000);
alphaAnimation.setFillAfter(true);
view.startAnimation(alphaAnimation);
實現了view從無透明度到完全透明的變化。
1.5 View的組合動畫
View的組合動畫通過AnimationSet類實現的,具體用法如下:
AnimationSet animationSet = new AnimationSet(true);
RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(alphaAnimation);
animationSet.setFillAfter(true);
animationSet.setDuration(2000);
view.startAnimation(animationSet);
AnimationSet的參數為true表示組合動畫公用一個插值器,什么是插值器呢?就是動畫速度的變化規律,常用的插值器如下:
LinearInterpolator:勻速
AccelerateInterpolator:加速
AccelerateDecelerateInterpolator:先加速再減速
DecelerateInterpolator:減速
BounceInterpolator:阻尼下落,即反彈數次后停止
可通過如下方式使用
RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
rotateAnimation.setFillAfter(true);
rotateAnimation.setInterpolator(new AccelerateInterpolator());
view.startAnimation(rotateAnimation);
最后,還可以通過setRepeatCount()、setRepeatMode()來設置動畫重復的次數、和重復模式,重復模式包括Animation.RESTART、Animation.REVERSE,即重新開始和逆序播放。
如果要監聽動畫的執行情況,則可以通過如下接口:
public static interface AnimationListener {
//開始
void onAnimationStart(Animation animation);
//結束
void onAnimationEnd(Animation animation);
//重復
void onAnimationRepeat(Animation animation);
}
2. xml方式實現
xml文件需要放到res目錄下的anim文件夾。
2.1 平移動畫
通過<translate >標簽實現。
實現View向上平移自身高度距離,即title隱藏效果:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fillAfter="true"
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="-100%" />
實現View向上平移50像素
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fillAfter="true"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="-50" />
2.2 縮放動畫
通過<scale >標簽實現。
實現View相對于自身中心,在x方向拉伸為原來2倍,在y方向縮小為原來0.5倍:
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="2.0"
android:toYScale="0.5" />
2.3 旋轉動畫
通過<scale >標簽實現。
實現View相對自身中心,瞬時間旋轉360度,同時逆序重復兩次:
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="2"
android:repeatMode="reverse"
android:toDegrees="360" />
2.4 透明度動畫
通過<alpha>標簽實現。
實現了View透明度從1.0f到0f的變化,同時是加速變化的:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="0" />
2.5 View的組合動畫
通過<set>標簽實現,但不能控制次序,只能同時發生,測試中發現,此時repeatCount屬相無效。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="5000"
android:fillAfter="true"
android:interpolator="@android:anim/linear_interpolator"
android:shareInterpolator="true">
<alpha
android:fromAlpha="1.0"
android:toAlpha="0" />
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" />
<scale
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="0.5" />
<translate
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="-100%" />
</set>
通過xml方式實現時,需要先通過loadAnimation()加載xml文件,如下:
Animation animation = AnimationUtils.loadAnimation(context, R.anim.set_anim);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(animation);
補間動畫在Android最初的版本就有了,在功能和可擴展方面都有相當大的局限性,例如平移效果只能改變View的顯示效果而已,并不能改變View真正的位置,舉個例子,如果將一個有點擊事件的Button從屏幕左上角移動到屏幕右上角,點擊右上角按鈕,發現并不能響應點擊事件,此時再點擊屏幕左上角竟然有響應。還有補間動畫只能作用于View,如果我們要對一個費View的對象進行動畫操作,那就無能為力了,正式因為種種功能上的缺陷,Android在3.0版本中引入了屬性動畫來進一步完善Android的動畫機制。
二、屬性動畫
有了屬性動畫,我們除了最基本的對View進行平移、縮放、旋轉、透明度操作外,還可以將動畫作用于指定的對象上,例如將一個Point對象從(0, 0)位置移動到(100, 100)位置。但是呢,有一點要注意,屬性動畫只能只能在Android3.0即以上版本使用,如果要兼容Android3.0以下版本,可以考慮使用大神JakeWharton的動畫庫:http://nineoldandroids.com/,但是這個庫在Android3.0以下的屬性動畫其實還是傳統的補間動畫哦!
屬性動畫中核心的兩個類就是ValueAnimator和ObjectAnimator,我們來了一個個看。
1.ValueAnimator是用來計算動畫對應屬性初始值和結束值之間的過渡的,類似于一個數值發生器,例如我們要實現數值0到數值2再到數值0在2000毫秒的過渡,可以這樣做:
ValueAnimator animator = ValueAnimator.ofFloat(0f, 2f, 0f);
//設置監聽器
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float curValue = (float) animation.getAnimatedValue();//當前值
float fraction = animation.getAnimatedFraction();//當前已過渡完成的比例
}
});
animator.setDuration(2000);//動畫時長
animator.start();
ValueAnimator.ofFloat()是實現浮點數的平滑過渡,如果需要整數的平滑過渡則可以使用ValueAnimator.ofInt(),用法基本一致。除了這兩個外還有 ValueAnimator.ofArgb()、 ValueAnimator.ofObject(),其中ValueAnimator.ofArgb()可以用來進行顏色值的過渡,ValueAnimator.ofObject()可以用來實現對象的過渡效果,這是就需要我們自行定義擴展了。
ValueAnimator.ofFloat()是如何實現過渡效果的呢?其實就是通過一個FloatEvaluator來完成的,不斷的計算當前的值:
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);
}
}
如果我們要從點Point(0, 0)過渡到點Point(100, 100),同樣也需要實現一個TypeEvaluator來計算當前的過渡屬性值:
public class PointEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
float x = startPoint.x + fraction * (endPoint.x - startPoint.x);
float y = startPoint.y + fraction * (endPoint.y - startPoint.y);
return new Point((int) x, (int) y);
}
}
很簡單,fraction代表已經過渡完成的比例,根據當前完成的比例、開始點和結束點計算出當前點的值。有了PointEvaluator就可以實現我們自己的Point過渡效果了:
Point startPoint = new Point(0, 0);
Point endPoint = new Point(100, 100);
ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Point currPoint = (Point) animation.getAnimatedValue();
}
});
這樣就通過TypeEvaluator實現了一個我們自定義的估值器。
2.ValueAnimator只是對值進行了一個平滑的動畫過渡,ObjectAnimator才是實現對任意對象的屬性進行動畫操作的,同是ObjectAnimator是ValueAnimator的子類。先看一下如何通過ObjectAnimator實現傳統補間動畫的四種效果:
2.1 平移動畫
要將一個View右移出屏幕,再移動回來,可以這樣做:
float translationX = view.getTranslationX();
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", translationX, 600f, translationX);
animator.setDuration(3000);
animator.start();
通過ofFloat()方法我們創建了一個ObjectAnimator對象,ofFloat()方法的第一個參數view就是要進行平移操作的對象,因為我們要對view進行平移操作,所以第二個參數傳入translationX,代表view的平移屬性,之后的參數是一個是可變長度的,個數根據你的需求控制。所以核心的參數就是第二個,根據這個屬性類型來區分對View進行何種動畫操作。
2.2 縮放動畫
同樣的道理,實現View的縮放效果可以這樣做:
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f, 1f);
animator.setDuration(5000);
animator.start();
我們將ofFloat()方法第二個參數換成了scaleX,表示在x方向對view進行縮放操作,這樣我們就實現了view拉伸兩倍再還原的效果。
2.3 旋轉動畫
例如,要將一個View旋轉360度可以這樣做,只要將ofFloat()第二個參數寫成rotation,起始、結束角度分別為0和360:
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);
animator.setDuration(3000);
animator.start();
2.4 透明度動畫
只要將ofFloat()第二個參數寫成alpha,則實現了View透明度從1到0在到0的變化:
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f);
animator.setDuration(5000);
animator.start();
2.5 顏色動畫
使用屬性動畫改變一個View的背景顏色也是可以的,如下代碼可以實現View背景色從藍到紅的變化:
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "backgroundColor", Color.BLUE, Color.RED);
animator.setEvaluator(new ArgbEvaluator());
animator.setDuration(5000);
animator.start();
2.6 組合動畫
和補間動畫類似,屬性動畫同樣可以將單個動畫進行組合,而且功能更強大,需要通過AnimatorSet類來實現,通過調用其play()方法得到一個AnimatorSet.Builder對象,Builder對象有一下四個方法:
after(Animator anim) 將現有動畫插入到傳入的動畫之后執行
after(long delay) 將現有動畫延遲指定毫秒后執行
before(Animator anim) 將現有動畫插入到傳入的動畫之前執行
with(Animator anim) 將現有動畫和傳入的動畫同時執行
我們要實現一個View從屏幕右側移入屏幕,然后旋轉360度同時有透明度變化,最后在水平方向拉伸兩倍后還原的效果可以這樣么做:
AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f);
ObjectAnimator rotation = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);
ObjectAnimator translation = ObjectAnimator.ofFloat(view, "translationX", 600f, 0f);
ObjectAnimator scale = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f, 1f);
animatorSet.play(alpha).with(rotation).after(translation).before(scale);
animatorSet.setDuration(5000);
animatorSet.start();
當然還可以setRepeatCount()、setRepeatMode()設置動畫的重復次數以及重復模式,重復模式有ValueAnimator.RESTART、ValueAnimator.REVERSE兩種。
3.除了通過代碼來編寫屬性動畫外,還可以使用xml的方式,xml文件需要放到res目錄下的animator文件夾。可用的標簽有以下三種:
- <animator> 代表ValueAnimator
- <objectAnimator> 代表ObjectAnimator
-
<set> 代表AnimatorSet
好了,看幾個例子:
要實現0到50平滑過渡的效果,可以這么做:
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:valueFrom="0"
android:valueTo="50"
android:valueType="floatType" />
要將一個View旋轉360度,可以這么做:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" />
要將一個View透明度從1變為0,可以這樣編寫:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType" />
平移和縮放動畫都是類似的。再看一下<set>標簽的用法:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<objectAnimator
android:duration="1500"
android:propertyName="translationX"
android:valueFrom="-500"
android:valueTo="0"
android:valueType="floatType" />
<set android:ordering="together">
<objectAnimator
android:duration="2000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" />
<set android:ordering="sequentially">
<objectAnimator
android:duration="1000"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType" />
<objectAnimator
android:duration="1000"
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" />
</set>
</set>
<set android:ordering="together">
<objectAnimator
android:duration="1500"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="2"
android:valueType="floatType" />
<objectAnimator
android:duration="1500"
android:propertyName="scaleX"
android:valueFrom="2"
android:valueTo="1"
android:valueType="floatType" />
</set>
</set>
我們實現了View先平移,然后同時進行旋轉和透明度變化,最后進行縮放的動畫效果。其中ordering屬相我們使用了sequentially、together兩種,分別代表順序播放和同時播放。還有以下兩個我們沒用到的屬性:startOffset:表示動畫的延時啟動時間,以及repeatCount、repeatMode。
使用xml動畫文件也是非常簡單的:
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
animator.setTarget(view);
animator.start();
4.Animator類當中提供了一個addListener()方法,可以用來監聽動畫的執行情況,無論ObjectAnimator、ValueAnimator還是AnimatorSet都是Animator的子類,所以它們都可以使用addListener():
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
如果想監聽其中的某些事件則可以通過AnimatorListenerAdapter來實現:
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
這樣我們只監聽了動畫的開始和結束事件。
5. 屬性動畫的插值器是兼容補間動畫的插值器的,所以補間動畫中的插值器完全可以在屬性動畫中使用。另外屬性動畫提供了一個TimeInterpolator接口,它的作用是根據時間流逝的百分比計算出當前屬性值改變的百分比,通過這個接口我們來自定義屬性動畫插值器:
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);
}
接口中只有一個getInterpolation()方法,其中input參數會根據動畫設置的時長在0到1之間勻速增長的變化。如果要自定義插值器可以這樣寫:
public class MyInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
float result = 0;
//todo
return result;
}
}
考驗數學功底的時候來了。。。。具體的實現細節可參考系統插值器。有一點需要注意,我們計算出來的result的值必須在0到1之間哦。
6. 回顧一下,我們在java代碼中可以通過ObjectAnimator.ofFloat()或ObjectAnimator.ofInt()來實現屬性動畫其中第二個參數可以是alpha、rotation、scaleX、translateX等等。為什么第二個參數可以是這些呢?這是因為ObjectAnimator內部的工作機制并不是對傳入的屬性名進行操作的,而是根據屬性名在當前子View類以及父類中去找對應的get和set方法,然后通過方法不斷地對值進行改變,從而實現動畫效果的,例如我們可以在View類中找到了參數rotation對應的get和set方法:
public float getRotation() {
return mRenderNode.getRotation();
}
public void setRotation(float rotation) {
if (rotation != getRotation()) {
// Double-invalidation is necessary to capture view's old and new areas
invalidateViewProperty(true, false);
mRenderNode.setRotation(rotation);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
既然如此,除了系統提供的屬性動畫外,如果要給一個自定義Button添加一個widths屬相動畫,實現其寬度的變化,如果使用translateX屬性會導致Button內容的拉伸,這并不是我們愿意看到的,所以我們自定義的widths屬相動畫并沒有這種問題。首先看我們自定義的Button類:
public class MyButton extends Button{
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public int getWidths(){
return getLayoutParams().width;
}
public void setWidths(int width){
getLayoutParams().width = width;
requestLayout();
}
}
因為我們規定屬性名為widths,所以我們提供了getWidths()、setWidths()兩個方法,當然這兩個方法也是必須的。接下來還需要編寫一個TypeEvaluator類來告訴系統寬度如何過渡:
public class WidthsEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startWidth = (int) startValue;
int endWidth = (int) endValue;
return (int)(startWidth + fraction * (endWidth - startWidth));
}
有了WidthsEvaluator類,我們需要的東西也就夠了,看下布局文件、以及使用方法:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.othershe.mybutton.MyButton
android:id="@+id/my_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="哎呦,不錯哦!" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="start"
android:text="開始" />
</RelativeLayout>
public class MainActivity extends AppCompatActivity {
private MyButton button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (MyButton) findViewById(R.id.my_btn);
}
public void start(View view) {
int width = button.getWidth();
ObjectAnimator animator = ObjectAnimator.ofObject(button, "widths", new WidthsEvaluator(), width, 600);
animator.setDuration(3000);
animator.start();
}
}
通過ObjectAnimator.ofObject()來調用的,很簡單,看下效果:
7. 通過java代碼實現屬性動畫除了通過ObjectAnimator類,還有另外一種方式,就是使用ViewPropertyAnimator類。例如我們要實現一個球形View自由落體的效果,可以這樣寫:
view.animate().x(0).y(500)
.setDuration(5000)
.setInterpolator(new BounceInterpolator());
通過view.animate()方法得到一個ViewPropertyAnimator對象,之后的操作都是基于該對象的方法,而且是鏈式調用的,同時在鏈尾系統會默認的添加start()方法,所以動畫會自動執行。僅僅是寫法的不同,根據喜好選擇吧,其它的方法有興趣的話可以自行測試。
三、幀動畫
幀動畫是順序的播放一系列圖片,從而產生動畫的效果,其實也就是圖片的切換。但是如果圖片過多、過大的話是很容易產生OOM的,所以使用時需要注意。
首先在res目錄下的drawable文件夾編寫一個xml文件:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">//
<item android:drawable="@mipmap/icon1" android:duration="300"/>
<item android:drawable="@mipmap/icon2" android:duration="300"/>
<item android:drawable="@mipmap/icon3" android:duration="300"/>
<item android:drawable="@mipmap/icon4" android:duration="300"/>
<item android:drawable="@mipmap/icon5" android:duration="300"/>
<item android:drawable="@mipmap/icon6" android:duration="300"/>
<item android:drawable="@mipmap/icon7" android:duration="300"/>
<item android:drawable="@mipmap/icon8" android:duration="300"/>
<item android:drawable="@mipmap/icon9" android:duration="300"/>
</animation-list>
oneshot屬性表示是否循環播放,值為true則只播放一次。
通如下方法調用,其中view是一個ImageView對象:
view.setImageResource(R.drawable.icons);
AnimationDrawable animationDrawable = (AnimationDrawable) view.getDrawable();
animationDrawable.start();
如果要停止播放可通過如下方法:
AnimationDrawable animationDrawable = (AnimationDrawable) view.getDrawable();
animationDrawable.stop();
到這里Android動畫相關的內容就結束了,足以應對開發中的使用場景了。