動畫 ViewPropertyAnimator 使用詳解和原理分析
本文轉載自微信公眾號「Android開發(fā)編程」,作者Android開發(fā)編程。轉載本文請聯(lián)系Android開發(fā)編程公眾號。
前言
平常所做的動畫大部分是針對View的,而View經常會需要集中動畫混合在一起做,因此提供了一個ViewPropertyAnimator類來快速的實現(xiàn)多個動畫的混合;
ViewPropertyAnimator從名字就可以看出是專用于View的屬性動畫,在API12被提供;
ViewPropertyAnimator專用于操作View動畫,語法更加簡潔,使用更加方便;
今天就來看看怎么用;
一、ViewPropertyAnimator使用詳解
1、獲取對象
ViewPropertyAnimator 沒有構造函數(shù),通過View.animate()方法可以方便的獲取;ViewPropertyAnimator 對象,此時獲取的動畫對象就專用于操作當前view;
- public ViewPropertyAnimator animate() {
- if (mAnimator == null) {
- mAnimator = new ViewPropertyAnimator(this);
- }
- return mAnimator;
- }
2、基本函數(shù)屬性介紹
- alpha(float value) 設置View的透明度,value最終值;
- alphaBy(float value) 設置View的透明度,value是在view當前值的基礎上的偏移量;rotation(float value):旋轉View,正值順時針,負值逆時針,value最終值;
- rotationBy(float value):旋轉,在當前值得基礎上偏移量;
- rotationX(float value):繞x軸旋轉;
- rotationXBy(float value):當View旋轉的基礎上以value為偏移量繞X軸旋轉;
- rotationY(float value):繞Y軸旋轉;
- rotationYBy(float value):在當前旋轉的基礎上繞Y軸旋轉;
- scaleX(float value):縮放view的X軸方向上的大小;
- scaleXBy(float value):當前View縮放的基礎上,在X軸方向上對view進行縮放;
- scaleY(float value):縮放view的Y軸方向上的大小;
- scaleYBy(float value):當前View縮放的基礎上,對view的Y軸方向進行縮放;
- translationX(float value):沿X軸方向平移,value大于0,X軸正方向;
- translationXBy(float value):帶有偏移量的平移;
- translationY(float value):沿Y軸方向平移,value大于0,沿Y軸正方向平移;
- translationYBy(float value) :在當前值的基礎上,在Y軸方向上平移;
- x(float value):在當前值的基礎上,修改view 的X坐標;
- xBy(float value):在當前值的基礎上,修改view 的X坐標;
- y(float value):在當前值的基礎上,修改View的Y的坐標;
- yBy(float value):在當前值的基礎上,修改View的Y的坐標;
- z(float value):在當前值的基礎上,修改View的Z的坐標;
- zBy(float value):在當前值的基礎上,修改View的Z的坐標;
3、基本使用
常用方法
- btnShow.animate()
- .setDuration(5000)
- //透明度
- .alpha(0)
- .alphaBy(0)
- //旋轉
- .rotation(360)
- .rotationBy(360)
- .rotationX(360)
- .rotationXBy(360)
- .rotationY(360)
- .rotationYBy(360)
- //縮放
- .scaleX(1)
- .scaleXBy(1)
- .scaleY(1)
- .scaleYBy(1)
- //平移
- .translationX(100)
- .translationXBy(100)
- .translationY(100)
- .translationYBy(100)
- .translationZ(100)
- .translationZBy(100)
- //更改在屏幕上的坐標
- .x(10)
- .xBy(10)
- .y(10)
- .yBy(10)
- .z(10)
- .zBy(10)
- //監(jiān)聽及其他設置
- .setInterpolator(new BounceInterpolator())
- .setStartDelay(1000)
- .setListener(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) {
- }
- })
- .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- }
- })
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- Log.i(TAG, "run: end");
- }
- })
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- Log.i(TAG, "run: start");
- }
- })
- .start();
4、添加監(jiān)聽
- setUpdateListener:添加動畫屬性變化監(jiān)聽
- setListener:添加動畫狀態(tài)監(jiān)聽
- ViewPropertyAnimator viewPropertyAnimator = gongxiang.animate().setDuration(3000).x(700).y(700).rotation(270).alpha(0.5f).setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- );
- }
- }).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- System.out.println("=========onAnimationEnd=======");
- }
- @Override
- public void onAnimationRepeat(Animator animation) {
- super.onAnimationRepeat(animation);
- }
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- System.out.println("=========onAnimationStart=======");
- }
- @Override
- public void onAnimationPause(Animator animation) {
- super.onAnimationPause(animation);
- }
- @Override
- public void onAnimationResume(Animator animation) {
- super.onAnimationResume(animation);
- }
- });
二、基本原理
1、執(zhí)行動畫基本步驟如下
- 通過test.animate()獲取ViewPropertyAnimator對象;
- 調用alpha、translationX等方法,返回當前ViewPropertyAnimator對象,可以繼續(xù)鏈式調用;
- alpha、translationX等方法內部最終調用animatePropertyBy(int constantName, float startValue, float byValue)方法;
- 在animatePropertyBy方法中則會將alpha、translationX等方法的操作封裝成NameVauleHolder,并將每個NameValueHolder對象添加到準備列表mPendingAnimations中;
- animatePropertyBy方法啟動mAnimationStarter,調用startAnimation,開始動畫;
- startAnimation方法中會創(chuàng)建一個ValueAnimator對象設置內部監(jiān)聽器;AnimatorEventListener,并將mPendingAnimations和要進行動畫的屬性名稱封裝成一個PropertyBundle對象,最后mAnimatorMap保存當前Animator和對應的PropertyBundle對象,該Map將會在animatePropertyBy方法和Animator監(jiān)聽器mAnimatorEventListener中使用,啟動動畫;
- 在動畫的監(jiān)聽器的onAnimationUpdate方法中設置所有屬性的變化值,并通過RenderNode類優(yōu)化繪制性能,最后刷新界面;
2、startAnimation()的源碼
- /**
- * Starts the underlying Animator for a set of properties. We use a single animator that
- * simply runs from 0 to 1, and then use that fractional value to set each property
- * value accordingly.
- */
- private void startAnimation() {
- if (mRTBackend != null && mRTBackend.startAnimation(this)) {
- return;
- }
- mView.setHasTransientState(true);
- //創(chuàng)建ValueAnimator
- ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
- //clone一份mPendingAnimations賦值給nameValueList
- ArrayList<NameValuesHolder> nameValueList =
- (ArrayList<NameValuesHolder>) mPendingAnimations.clone();
- //賦值完后清空
- mPendingAnimations.clear();
- //用于標識要執(zhí)行動畫的屬性
- int propertyMask = 0;
- int propertyCount = nameValueList.size();
- //遍歷所有nameValuesHolder,取出其屬性名稱mNameConstant,
- //執(zhí)行"|"操作并最終賦值propertyMask
- for (int i = 0; i < propertyCount; ++i) {
- NameValuesHolder nameValuesHolder = nameValueList.get(i);
- propertyMask |= nameValuesHolder.mNameConstant;
- }
- //創(chuàng)建PropertyBundle,并添加到mAnimatorMap中
- mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
- if (mPendingSetupAction != null) {
- //設置硬件加速
- mAnimatorSetupMap.put(animator, mPendingSetupAction);
- mPendingSetupAction = null;
- }
- if (mPendingCleanupAction != null) {
- //移除硬件加速
- mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
- mPendingCleanupAction = null;
- }
- if (mPendingOnStartAction != null) {
- //設置開始的動畫(監(jiān)聽器的開始方法中調用)
- mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
- mPendingOnStartAction = null;
- }
- if (mPendingOnEndAction != null) {
- //設置結束后要進行的下一個動畫(監(jiān)聽器的結束方法中調用)
- mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
- mPendingOnEndAction = null;
- }
- //添加內部監(jiān)聽器
- animator.addUpdateListener(mAnimatorEventListener);
- animator.addListener(mAnimatorEventListener);
- //判斷是否延長開始
- if (mStartDelaySet) {
- animator.setStartDelay(mStartDelay);
- }
- //執(zhí)行動畫的實現(xiàn)
- if (mDurationSet) {
- animator.setDuration(mDuration);
- }
- //設置插值器
- if (mInterpolatorSet) {
- animator.setInterpolator(mInterpolator);
- }
- //開始執(zhí)行動畫
- animator.start();
- }
- 創(chuàng)建Animator,變化值從0到1,設置內部監(jiān)聽器mAnimatorEventListener;
- clone一份mPendingAnimations列表,并計算屬性值標記propertyMask,封裝成PropertyBundle對象;
- 使用mAnimatorMap保存當前Animator和對應的PropertyBundle對象;
- 該Map將會在animatePropertyBy方法和Animator監(jiān)聽器mAnimatorEventListener中使用;
- 啟動animator動畫;
2、PropertyBundle
- private static class PropertyBundle {
- int mPropertyMask;
- ArrayList<NameValuesHolder> mNameValuesHolder;
- PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
- mPropertyMask = propertyMask;
- mNameValuesHolder = nameValuesHolder;
- }
- boolean cancel(int propertyConstant) {
- if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
- int count = mNameValuesHolder.size();
- for (int i = 0; i < count; ++i) {
- NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
- if (nameValuesHolder.mNameConstant == propertyConstant) {
- mNameValuesHolder.remove(i);
- mPropertyMask &= ~propertyConstant;
- return true;
- }
- }
- }
- return false;
- }
- }
PropertyBundle:內部類,存放著將要執(zhí)行的動畫的屬性集合信息,每次調用animator.start();
都會將存放在mPendingAnimations的clone一份存入PropertyBundle的內部變量mNameValuesHolder中,然后再將遍歷mPendingAnimations中的NameValueHolder類,取出要執(zhí)行的屬性進行”|”操作;
最后記錄成一個mPropertyMask的變量,存放在PropertyBundle中,PropertyBundle就是最終要執(zhí)行動畫的全部屬性的封裝類;
3、AnimatorEventListener
- private class AnimatorEventListener
- implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
- ...
- ...
- }
ViewPropertyAnimator內部的監(jiān)聽器:這個類實現(xiàn)了Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener接口;
這個類還有一個onAnimationUpdate()的監(jiān)聽方法,它是動畫執(zhí)行的關鍵所在;
4、mAnimatorMap
- private HashMap<Animator, PropertyBundle> mAnimatorMap =
- new HashMap<Animator, PropertyBundle>();
- mAnimatorMap:存放PropertyBundle類的Map,這個Map中存放的是正在執(zhí)行的動畫的PropertyBundle,這個PropertyBundle包含這本次動畫的所有屬性的信息;
- 最終在AnimatorEventListener的onAnimationUpdate()方法中會通過這個map獲取相應的屬性,然后不斷更新每幀的屬性值以達到動畫效果;
- 通過前面對animatePropertyBy方法的分析,我們可以知道該Map會保證當前只有一個Animator對象對該View的屬性進行操作,不會存在兩個Animator在操作同一個屬性;
5、onAnimationUpdate
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- //取出當前Animator對應用propertyBundle對象
- PropertyBundle propertyBundle = mAnimatorMap.get(animation);
- if (propertyBundle == null) {
- // Shouldn't happen, but just to play it safe
- return;
- }
- //是否開啟了硬件加速
- boolean hardwareAccelerated = mView.isHardwareAccelerated();
- // alpha requires slightly different treatment than the other (transform) properties.
- // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
- // logic is dependent on how the view handles an internal call to onSetAlpha().
- // We track what kinds of properties are set, and how alpha is handled when it is
- // set, and perform the invalidation steps appropriately.
- boolean alphaHandled = false;
- if (!hardwareAccelerated) {
- mView.invalidateParentCaches();
- }
- //取出當前的估算值(插值器計算值)
- float fraction = animation.getAnimatedFraction();
- int propertyMask = propertyBundle.mPropertyMask;
- if ((propertyMask & TRANSFORM_MASK) != 0) {
- mView.invalidateViewProperty(hardwareAccelerated, false);
- }
- //取出所有要執(zhí)行的屬性動畫的封裝對象NameValuesHolder
- ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
- if (valueList != null) {
- int count = valueList.size();
- //遍歷所有NameValuesHolder,計算變化值,并設置給對應的屬性
- for (int i = 0; i < count; ++i) {
- NameValuesHolder values = valueList.get(i);
- float value = values.mFromValue + fraction * values.mDeltaValue;
- if (values.mNameConstant == ALPHA) {
- alphaHandled = mView.setAlphaNoInvalidation(value);
- } else {
- setValue(values.mNameConstant, value);
- }
- }
- }
- if ((propertyMask & TRANSFORM_MASK) != 0) {
- if (!hardwareAccelerated) {
- mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
- }
- }
- // invalidate(false) in all cases except if alphaHandled gets set to true
- // via the call to setAlphaNoInvalidation(), above
- if (alphaHandled) {
- mView.invalidate(true);
- } else {
- mView.invalidateViewProperty(false, false);
- }
- if (mUpdateListener != null) {
- mUpdateListener.onAnimationUpdate(animation);
- }
- }
取出當前Animator對應用propertyBundle對象并獲取當前的估算值(插值器計算值),用于后續(xù)動畫屬性值的計算;
從propertyBundle取出要進行動畫的屬性列表 ArrayList
遍歷所有NameValuesHolder,計算變化值,并通過setValue設置給對應的屬性,如果是ALPHA,則會特殊處理一下,最終形成動畫效果;
總結
年底了,有疫情有裁員,大家要努力,一起加油