深入學習屬性動畫
1.動畫三種方式
動畫名稱 | 動畫的定義 |
逐幀動畫 | 將一張完整的圖片拆成一張張圖片單獨的播放 |
補間動畫 | 可以對view進行一些列的動畫操作 |
屬性動畫 | 可以對非view的對象就行動畫操作,可以改變view的屬性而不單單是改變view的動畫效果 |
2.屬性動畫的學習
1.ValueAnimator
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
anim.start();
valueAnimatior會自動的從初始值,過度到結束值這樣的效果,還負責管理動畫的播放次數。
播放模式。
監聽動畫的執行:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (float) animation.getAnimatedValue();
Log.d("TAG", "cuurent value is " + currentValue);
}
});
anim.start();
2.ObjectAnimator
ValueAnimator知不是對值的平滑的動畫過渡而ObjectAnimator可以直接對任意對象任意屬性進行
動畫操作。ObjectAnimatior繼承自ValueAnimator用法類似,將一個TextView在五秒內從常規變成透明再變成常規:
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
animator.setDuration(5000);
animator.start();
3.組合動畫
實現組合動畫主要用到AnimatorSet類,這個類提供了一個play()的方法,返回一個AnimatorSet實例
AnimatorSet.Builder中包括四個方法
after(Animator anim),將現有動畫插入到傳入的動畫之後
after(long delay),將現有動畫延遲到制定動畫之後
before(Animator anim),將現有動畫插入到傳入的動畫之前
with(Animator anim),將現有動畫和傳入的動畫同時執行。
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f); ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f); ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f); AnimatorSet animSet = new AnimatorSet(); animSet.play(rotate).with(fadeInOut).after(moveIn); animSet.setDuration(5000); animSet.start();
4.Animator監聽器
監聽動畫何時開始,何時結束,Animator類提供了一個addListener()方法,這個方法接受一個AnimatorListener。添加一個監聽器的示例代碼:
anim.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
});
有時並不需要監聽這麼多事件,爲此提供了一個AnimatorLIstenerAdapter,解決。示例代碼:
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
}
});
5.使用XML編寫動畫
如果使用XML來編寫動畫,需要在res目錄下創建一個animator文件,在XML文件中添加三種標籤
<animator>
標籤對應代碼中的ValueAnimator:實現一個而從0到100的平滑過渡<animator xmlns:android="http://schemas.android.com/apk/res/android" android:valueFrom="0" android:valueTo="100" android:valueType="intType"/>
<objectAnimator>
對應代碼的ObjectAnimator;實現透明度從1到0的動畫<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" android:propertyName="alpha"/>
<set>
對應代碼的AnimatorSet
Xml文件編寫之後在代碼中的加載方式:Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file); animator.setTarget(view); animator.start();
ValueAnimator的高級用法
在自定義一個View的時候,在這個View中有Point對象管理座標,然後在onDraw()方法中就是根據這個Point對象的座標值,進行繪製,如果我們對整個自定義View的動畫效果就有了。
先了解TypeEvaluator的用法:簡單來說就是告訴動畫系統,如何從初始值過渡到結束值。比如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);
}
}
三個參數的意義:
fraction,表示動畫的完成度。
startValue 動畫的初始值
endValue 動畫的結束值
ObjectAnimator的高級用法
補間動畫只能實現移動,縮放等操作,但是要對view的顏色進行動態改變就無法實現。
ObjectAnimator:內部的工作機制是通過尋找特定的屬性的get和set方法,然後通過對值不斷的改變從而實現動畫。比如,我們自定義一個View,只需要在其內部這樣寫:
public class MyAnimView extends View {
...
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
mPaint.setColor(Color.parseColor(color));
invalidate();
}
...
}
Interpolator的用法
Interpolator:直譯是補間器的意思。它的主要作用是可以控制動畫的變化速率,比如去實現一種非線性運動的動畫效果。那麼什麼叫做非線性運動的動畫效果呢?就是說動畫改變的速率不是一成不變的,像加速運動以及減速運動都屬於非線性運動。不過Interpolator並不是屬性動畫中新增的技術,實際上從Android 1.0版本開始就一直存在Interpolator接口了,而之前的補間動畫當然也是支持這個功能的。只不過在屬性動畫中新增了一個TimeInterpolator接口,這個接口是用於兼容之前的Interpolator的,這使得所有過去的Interpolator實現類都可以直接拿過來放到屬性動畫當中使用 。
AccelerateDecelerateInterpolator 先加速後減速的interpolator,系統默認的補間器。
AccelerateInterpolator 加速的補間器
DecelerateInterpolator 減速的補間器。
系統內置的補間器非常多。interpolator的內部實現機制是什麼:/** * A time interpolator defines the rate of change of an animation. This allows animations * to have non-linear motion, such as acceleration and deceleration. */ 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即:動畫開始的時候input值爲0,動畫結束的時候input值爲1。inuput值與fraction值之間有什麼關係?input值就是fraction。input值經過系統計算,傳入到getINnterpolation中,然後我們可以自己實現這個方法,經過計算返回input值,這個值就是fraction參數。因此最簡單的就是input值與fraction相同。系統中內置LinearInterpolator就是一種勻速運動的Interpolator,那麼我們來看一下它的源碼是怎麼實現的:
/** * An interpolator where the rate of change is constant */ @HasNativeInterpolator public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { public LinearInterpolator() { } public LinearInterpolator(Context context, AttributeSet attrs) { } public float getInterpolation(float input) { return input; } /** @hide */ @Override public long createNativeInterpolator() { return NativeInterpolatorFactoryHelper.createLinearInterpolator(); } }
知道補間器裏面的實現過程自定義一個先加速,後減速的補間器。
/** * @author [email protected] * @Company 杭州木瓜科技有限公司 * @date 2016/8/3 */ public class DefineInterpolation implements TimeInterpolator { @Override public float getInterpolation(float input) { if (input<0.5){ input= (float) ((Math.sin(input*Math.PI)))/2; }else { input= (float) (2-Math.sin(input*Math.PI))/2; } return input; }