本篇博客記錄一下Android屬性動畫的基本用法。
Android提供的屬性動畫機制,可以很簡單地調整視圖的屬性值,形成動畫效果。例如:
.............//在3s時間內,將mSunView沿著y軸從start移動到end的位置//其實現的原理是,不斷調用mSunView.setY接口,設置其縱坐標//縱坐標的值,逐漸從start變化到endObjectAnimator heightAnimator = ObjectAnimator .ofFloat(mSunView, "y", start, end) .setDuration(3000);//也可以設置插值器,例如逐漸加速等heightAnimator.setInterpolator(new AccelerateInterpolator());//動畫開始heightAnimator.start();............ObjectAnimator將根據插值器的規則,將屬性值從start逐漸變化到end。
不過有的屬性值并不適合逐漸變化,例如顏色。 我們知道顏色是用類似于#fcfcb716這種16進制的數字表示的,如果逐漸增加數字,反而會帶來劇烈的色彩變化。 此時ObjectAnimator需要借助TypeEvalutor的子類,精確地計算開始到結束間的遞增指。例如:
.............ObjectAnimator skyAnimator = ObjectAnimator .ofInt(mSkyView, "backgroundColor", start, end) .setDuration(duration);//借助于ArgbEvaluator,精確調整色彩變化的遞進值skyAnimator.setEvaluator(new ArgbEvaluator());skyAnimator.start();.............當需要同時調整多個屬性時,可以使用PRopertyValuesHolder,例如:
................//沿著x軸縮放,縮放比例從start到endPropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", start, end);//沿著y軸縮放,縮放比例從start到endPropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", start, end);//利用ofPropertyValuesHolder接口,可以傳入View對象及多個PropertyValuesHolder//當該動畫開始時,mSunView多個的將同時沿著x、y軸縮放ObjectAnimator.ofPropertyValuesHolder(mSunView, pvhX, pvhY) .setDuration(3000) .start();..........為了達到同樣的效果,也可以使用AnimatorUpdateListener,例如:
...............ObjectAnimator scaleAnimator = ObjectAnimator //使用一個不存在的屬性,mSunView并不會發生實際的改變 //但生成的值會逐漸從start變化到end .ofFloat(mSunView, "whatever", start, end) .setDuration(3000);//增加AnimatorUpdateListener,生成的值變化時,就會回調onAnimationUpdate接口scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //得到變化的值 float val = (float)animation.getAnimatedValue(); //調整屬性 mSunView.setScaleX(val); mSunView.setScaleY(val); }});scaleAnimator.start();..............當需要多個屬性先后變化時,就可以AnimatorSet了。 AnimatorSet就是可以放在一起執行的動畫集,其使用方法類似于:
...................@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void startAnimation(boolean isSunSet) { //創建動畫集 mAnimatorSet = new AnimatorSet(); //heightAnimator將和initialSkyAnimator、scaleAnimator、rotateAnimator同時播放 mAnimatorSet.play(getHeightAnimator(isSunSet)) .with(getInitialSkyAnimator(isSunSet)) .with(getScaleAnimator(isSunSet)) .with(getRotateAnimator()) //先于laterSkyAnimator .before(getLaterSkyAnimator(isSunSet)); mAnimatorSet.start();}@RequiresApi(api = Build.VERSION_CODES.KITKAT)private ObjectAnimator getHeightAnimator(boolean isSunset) { float sunYStart = mSunView.getTop(); float sunYEnd = mSkyView.getHeight() + mSunView.getBottom() - mSunView.getTop(); final float start = isSunset ? sunYStart : sunYEnd; final float end = isSunset ? sunYEnd : sunYStart; ObjectAnimator heightAnimator = ObjectAnimator .ofFloat(mSunView, "y", start, end) .setDuration(3000); heightAnimator.setInterpolator(new AccelerateInterpolator()); return heightAnimator;}private ObjectAnimator getInitialSkyAnimator(boolean isSunset) { int start = isSunset ? mBlueSkyColor : mNightSkyColor; int end = mSunsetSkyColor; return createSkyAnimator(start, end, 3000);}private ObjectAnimator getLaterSkyAnimator(boolean isSunset) { int start = mSunsetSkyColor; int end = isSunset ? mNightSkyColor : mBlueSkyColor; return createSkyAnimator(start, end, 1500);}private ObjectAnimator createSkyAnimator(int start, int end, int duration) { ObjectAnimator skyAnimator = ObjectAnimator .ofInt(mSkyView, "backgroundColor", start, end) .setDuration(duration); skyAnimator.setEvaluator(new ArgbEvaluator()); return skyAnimator;}private ObjectAnimator getScaleAnimator(boolean isSunSet) { float start = (float) (isSunSet ? 1 : 1.5); float end = (float) (isSunSet ? 1.5 : 1); PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", start, end); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", start, end); return ObjectAnimator.ofPropertyValuesHolder(mSunView, pvhX, pvhY) .setDuration(3000); return scaleAnimator;}private ObjectAnimator getRotateAnimator() { ObjectAnimator objectAnimator = ObjectAnimator .ofFloat(mSunView, "rotation", 0, 360) .setDuration(1000); objectAnimator.setRepeatCount(4); return objectAnimator;}.................AnimatorSet繼承Animator,具有同樣的接口判斷動畫執行的狀態,例如:
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_sunset, container, false); .............. v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (Build.VERSION.SDK_INT >= 19) { //isStarted判斷動畫是否已經開始(動畫暫停時,isStarted返回的也是true) if (mAnimatorSet == null || !mAnimatorSet.isStarted()) { startAnimation(); } else { //isPaused判斷動畫是否暫停 if (mAnimatorSet.isPaused()) { //resume繼續播放動畫 mAnimatorSet.resume(); } else { //pause暫停播放動畫 mAnimatorSet.pause(); } } } } }); return v;}與上述狀態變化接口對應,Animator及其子類均可以利用AnimatorListener監聽動畫的狀態:
..............mAnimatorSet.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) { .............. }});Animator運行到不同的狀態時,將回調AnimatorListener相應的接口。 例如,利用AnimatorListener也可以做到一個動畫結束時,啟動下一個動畫。 只要在AnimatorListener.onAnimationEnd中啟動下一個動畫即可。
假設現在有個需求,需要點擊屏幕后,逆向播放已經放過的動畫。 即一個View從高度A下降到B時,點擊屏幕,View從B上升到A。
顯然動畫包含的屬性越是復雜,完全逆向就越困難。 不過實現的思路大概是,保存初始和暫停的狀態,然后構造逆向的ObjectAnimator,示例如下:
................private long mCurrentPlayTime = 0;private float mCurrentHeight = 0;@RequiresApi(api = Build.VERSION_CODES.KITKAT)private ObjectAnimator getHeightAnimator(boolean isSunset) { float sunYStart = mSunView.getTop(); float sunYEnd = mSkyView.getHeight() + mSunView.getBottom() - mSunView.getTop(); final float start = isSunset ? sunYStart : sunYEnd; final float end = isSunset ? sunYEnd : sunYStart; ObjectAnimator heightAnimator = ObjectAnimator .ofFloat(mSunView, "y", start, end) .setDuration(3000); heightAnimator.setInterpolator(new AccelerateInterpolator()); //利用AnimatorUpdateListener記錄動畫執行時的中間狀態 heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurrentPlayTime = animation.getCurrentPlayTime(); mCurrentHeight = (float)animation.getAnimatedValue(); } }); heightAnimator.addPauseListener(new Animator.AnimatorPauseListener() { @Override public void onAnimationPause(Animator animation) { heightAnimator = ObjectAnimator //從當前位置變化到初始位置 .ofFloat(mSunView, "y", mCurrentHeight, start) //執行時間為已經播放的時間 .setDuration(mCurrentPlayTime); //逐漸減速 heightAnimator.setInterpolator(new DecelerateInterpolator()); //取消之前的動畫 //這里只是示范一下,如果整個AnimatorSet逆向 //則因該在AnimatorSet的AnimatorPauseListener中重構整個AnimatorSet mAnimatorSet.cancel(); //開始新的動畫 heightAnimator.start(); } @Override public void onAnimationResume(Animator animation) { } }); return heightAnimator;}.........以上就是屬性動畫的一些基本用法,以后遇到新的知識再作進一步補充。
P.S. : 最后補充一下,視圖中各個屬性的基本含義: 


新聞熱點
疑難解答