AlphaAnimation(透明度動畫)、RotateAnimation(旋轉動畫)、ScaleAnimation(縮放動畫)、TranslateAnimation(平移動畫)
且只能做用於View上。 (應用如Activity切換動畫)改變的只是View的畫布
,而沒有改變View的點擊響應區域;而屬性動畫會經過反射技術來獲取和執行屬性的get、set方法,從而改變了對象位置的屬性值
。經過畫布的移動實現動畫
),這就是爲何Animation不會改變View的屬性的根本所在。咱們能夠理解爲Animation只是操做的View畫布而並非改變View的位置(mLeft,mRight,mTop,mBottom)。在View的draw()方法中:java
final Animation a = getAnimation();//是否設置了動畫
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
} else {
if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) {
// No longer animating: clear out old animation matrix
mRenderNode.setAnimationMatrix(null);
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
if (!drawingWithRenderNode
&& (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
final Transformation t = parent.getChildTransformation();
final boolean hasTransform = parent.getChildStaticTransformation(this, t);
if (hasTransform) {
final int transformType = t.getTransformationType();
transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
}
}
}
......
if (transformToApply != null) {
if (concatMatrix) {
if (drawingWithRenderNode) {
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
......
}
複製代碼
在View的draw方法中咱們能夠看到當咱們設置了動畫以後會生成transformToApply對象,當transformToApply不爲null的時候會進行根據動畫的參數矩陣進行View的從新繪製。重點看到Animation產生的動畫數據實際並非應用在View自己的,而是應用在RenderNode或者Canvas上的,這就是爲何Animation不會改變View的屬性的根本所在。另外一方面,咱們知道Animation僅在View被繪製的時候才能發揮本身的價值,這也是爲何補間動畫被放在Android.view包內。git
插值器(Interpolator):根據時間流逝的百分比計算出當前屬性值改變的百分比。肯定了動畫效果變化的模式,如勻速變化、加速變化等等。View動畫和屬性動畫都可使用。(可理解爲改變更畫的速度曲線,默認使用的是先加速再減速的插值器
)github
經常使用的系統內置插值器:canvas
自定義插值器實現Interpolator接口。bash
public class LinearInterpolator implements Interpolator {
public LinearInterpolator() {
}
public LinearInterpolator(Context context,AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
}
複製代碼
類型估值器(TypeEvaluator):根據當前屬性改變的百分比計算出改變後的屬性值(計算在百分之多少的時候返回什麼值
)。針對於屬性動畫,View動畫不須要類型估值器。經常使用的系統內置的估值器:app
自定義估值器實現TypeEvaluator接口。ide
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction,Integer startValue,Integer endValue) {
int startInt = startValue; start:1,end:3 那麼在0.2的時候則爲: 1+(3-1)*0.2
return (int)(startInt + fraction * (endValue -startInt));
}
}
複製代碼
如下爲例:動畫
mView.setOnTouchListener(new View.OnTouchListener() {
int lastX, lastY;
Toast toast = Toast.makeText(TestActivity.this, "", Toast.LENGTH_SHORT);
@Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
if (event.getAction() == MotionEvent.ACTION_MOVE) {
//Toolbar和狀態欄的高度
int toolbarHeight = (getWindow().getDecorView().getHeight() - findViewById(R.id.root_view).getHeight());
int widthOffset = mView.getWidth() / 2;
int heightOffset = mView.getHeight() / 2;
mView.setTranslationX(x - mView.getLeft() - widthOffset);
mView.setTranslationY(y - mView.getTop() - heightOffset - toolbarHeight);
toast.setText(String.format("left: %d, top: %d, right: %d, bottom: %d",
mView.getLeft(), mView.getTop(), mView.getRight(), mView.getBottom()));
toast.show();
}
lastX = x;
lastY = y;
return true;
}
});
複製代碼
當咱們調用了setTranslationX或setRotation等方法後其實改變的並非View的真正位置,只是對View畫布的改變。而改變View真正座標只能使用view.layout()
方法,那麼爲何屬性動畫能夠經過setTranslationX等方法改變View的點擊事件區域呢?ui
由於在事件分發中當咱們去判斷View是否在手指點擊區域內的時候會去判斷View是否調用了setTranslation,setRotation,setScale這些方法,若是調用的話會調用matrix.mapPoints
這個方法將View的初始座標值和通過動畫改變的座標值進行一個融合計算從而獲得最終的View座標值,以此值去判斷View是否再點擊區域內,而補間動畫並無將矩陣設置給View,那麼最終作座標融合的時候天然不會以融合改變後的座標去判斷View是否在手指點擊區域內,因此View動畫不會改變點擊區域而屬性動畫能夠。this
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
......
Transformation transformToApply = null;
final Animation a = getAnimation();
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
transformToApply = parent.getChildTransformation();
}
if (transformToApply != null) {
if (drawingWithRenderNode) { //屬性動畫會設置矩陣
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
canvas.translate(-transX, -transY); //View動畫不會設置到矩陣中
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
}
}
}
複製代碼
當咱們設置的動畫播放補間動畫的時候,咱們所看到的變化,都只是臨時
的。而屬性動畫呢,它所改變的東西,卻會更新到這個View所對應的矩陣中
,因此當ViewGroup分派事件的時候,會正確的將當前觸摸座標,轉換成矩陣變化後的座標,這就是爲何播放補間動畫不會改變觸摸區域的緣由了。
public AnimationHandler getAnimationHandler() { return AnimationHandler.getInstance();}
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);this就是ValueAnimator自身
}
複製代碼
如上當咱們新建ValueAnimator的時候會建立AnimationHandler這個靜態類,同時它會持有ValueAnimator,當進入Activity界面後若是有一些和控件綁定在一塊兒的屬性動畫在運行同時設置成了無限循環模式
,退出的時候要記得cancel
掉這些動畫不然會形成內存泄漏。
引用鏈關係:Activity->View->ValueAnimator->AnimationHandler(靜態類,GCROOT)
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
複製代碼
裏邊包括各類類型的動畫使用,關鍵幀等的使用實例。 下邊給一個屬性動畫的使用實例
ValueAnimator mFirstPhaseAnimator;
if (mFirstPhaseAnimator == null) {
mFirstPhaseAnimator = ValueAnimator.ofInt(0, 100);
mFirstPhaseAnimator.setDuration(2000);
mFirstPhaseAnimator.setInterpolator(new DecelerateInterpolator());
mFirstPhaseAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
if (!mIsAnimationSetFinished) {
mCurrentVal = (int) valueAnimator.getAnimatedValue(); 獲取當前的值
if (mProcessor != null) {
String text = mProcessor.getText(getContext(), mCurrentVal);
if (TextUtils.isEmpty(text)) {
setValText(text);
}
} else {
setValText(String.valueOf(mCurrentVal));
}
}
}
});
mFirstPhaseAnimator.start();//開啓動畫
}
複製代碼