动画 ViewPropertyAnimator 使用详解和原理分析

焦点2025-11-05 08:36:184

本文转载自微信公众号「Android开发编程」,动画作者Android开发编程。使用转载本文请联系Android开发编程公众号。详解析

前言

平常所做的和原动画大部分是针对View的,而View经常会需要集中动画混合在一起做,理分因此提供了一个ViewPropertyAnimator类来快速的动画实现多个动画的混合;

ViewPropertyAnimator从名字就可以看出是专用于View的属性动画,在API12被提供;

ViewPropertyAnimator专用于操作View动画,使用语法更加简洁,详解析使用更加方便;

今天就来看看怎么用;

一、和原ViewPropertyAnimator使用详解

1、理分获取对象

ViewPropertyAnimator 没有构造函数,动画通过View.animate()方法可以方便的使用获取;ViewPropertyAnimator 对象,此时获取的详解析动画对象就专用于操作当前view;

public ViewPropertyAnimator animate() {     if (mAnimator == null) {         mAnimator = new ViewPropertyAnimator(this);     }     return mAnimator; } 

2、基本函数属性介绍

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)                         //监听及其他设置                         .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、添加监听

setUpdateListener:添加动画属性变化监听 setListener:添加动画状态监听 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、执行动画基本步骤如下

通过test.animate()获取ViewPropertyAnimator对象; 调用alpha、translationX等方法,返回当前ViewPropertyAnimator对象,可以继续链式调用; alpha、translationX等方法内部最终调用animatePropertyBy(int constantName, float startValue, float byValue)方法; 在animatePropertyBy方法中则会将alpha、translationX等方法的操作封装成NameVauleHolder,并将每个NameValueHolder对象添加到准备列表mPendingAnimations中; animatePropertyBy方法启动mAnimationStarter,调用startAnimation,开始动画; startAnimation方法中会创建一个ValueAnimator对象设置内部监听器;AnimatorEventListener,并将mPendingAnimations和要进行动画的属性名称封装成一个PropertyBundle对象,最后mAnimatorMap保存当前Animator和对应的PropertyBundle对象,该Map将会在animatePropertyBy方法和Animator监听器mAnimatorEventListener中使用,启动动画; 在动画的监听器的onAnimationUpdate方法中设置所有属性的变化值,并通过RenderNode类优化绘制性能,最后刷新界面;

2、startAnimation()的b2b供应网源码

/**  * 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);     //创建ValueAnimator     ValueAnimator animator = ValueAnimator.ofFloat(1.0f);     //clone一份mPendingAnimations赋值给nameValueList     ArrayList<NameValuesHolder> nameValueList =             (ArrayList<NameValuesHolder>) mPendingAnimations.clone();      //赋值完后清空     mPendingAnimations.clear();     //用于标识要执行动画的属性     int propertyMask = 0;     int propertyCount = nameValueList.size();     //遍历所有nameValuesHolder,取出其属性名称mNameConstant,     //执行"|"操作并最终赋值propertyMask     for (int i = 0; i < propertyCount; ++i) {         NameValuesHolder nameValuesHolder = nameValueList.get(i);         propertyMask |= nameValuesHolder.mNameConstant;     }     //创建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) {         //设置开始的动画(监听器的开始方法中调用)         mAnimatorOnStartMap.put(animator, mPendingOnStartAction);         mPendingOnStartAction = null;     }     if (mPendingOnEndAction != null) {         //设置结束后要进行的下一个动画(监听器的结束方法中调用)         mAnimatorOnEndMap.put(animator, mPendingOnEndAction);         mPendingOnEndAction = null;     }     //添加内部监听器     animator.addUpdateListener(mAnimatorEventListener);     animator.addListener(mAnimatorEventListener);     //判断是否延长开始     if (mStartDelaySet) {         animator.setStartDelay(mStartDelay);     }     //执行动画的实现     if (mDurationSet) {         animator.setDuration(mDuration);     }     //设置插值器     if (mInterpolatorSet) {         animator.setInterpolator(mInterpolator);     }     //开始执行动画     animator.start(); }  创建Animator,变化值从0到1,设置内部监听器mAnimatorEventListener; clone一份mPendingAnimations列表,并计算属性值标记propertyMask,封装成PropertyBundle对象; 使用mAnimatorMap保存当前Animator和对应的PropertyBundle对象; 该Map将会在animatePropertyBy方法和Animator监听器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:内部类,存放着将要执行的动画的属性集合信息,每次调用animator.start();

都会将存放在mPendingAnimations的clone一份存入PropertyBundle的内部变量mNameValuesHolder中,然后再将遍历mPendingAnimations中的NameValueHolder类,取出要执行的属性进行”|”操作;

最后记录成一个mPropertyMask的变量,存放在PropertyBundle中,PropertyBundle就是最终要执行动画的全部属性的封装类;

3、AnimatorEventListener

private class AnimatorEventListener             implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { ... ... } 

ViewPropertyAnimator内部的监听器:这个类实现了Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener接口;

这个类还有一个onAnimationUpdate()的监听方法,它是动画执行的关键所在;

4、mAnimatorMap

private HashMap<Animator, PropertyBundle> mAnimatorMap =         new HashMap<Animator, PropertyBundle>();  mAnimatorMap:存放PropertyBundle类的Map,这个Map中存放的是正在执行的动画的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) {                 // Shouldnt 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);             }             //取出所有要执行的属性动画的封装对象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对象并获取当前的估算值(插值器计算值),用于后续动画属性值的计算;

从propertyBundle取出要进行动画的属性列表 ArrayList valueList;

遍历所有NameValuesHolder,计算变化值,并通过setValue设置给对应的属性,如果是ALPHA,则会特殊处理一下,最终形成动画效果;

总结

年底了,有疫情有裁员,大家要努力,一起加油

本文地址:http://www.bzuk.cn/news/340e30099359.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

热门文章

全站热门

大白菜GPT分区教程(详解大白菜GPT分区教程,帮助您轻松完成硬盘分区操作)

台式电脑扩展显示教程(一步步教你如何扩展台式电脑的显示屏,让工作效率翻倍)

1、/etc/profile:在登录时,操作系统定制用户环境时使用的第一个文件,此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行。2、/etc/environment:在登录时操作系统使用的第二个文件,系统在读取你自己的profile前,设置环境文件的环境变量。3、~/.bash_profile:在登录时用到的第三个文件是.profile文件,每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该 文件仅仅执行一次!默认情况下,他设置一些环境变游戏量,执行用户的.bashrc文件。4、/etc/bashrc:为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取。5、~/.bashrc:该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该该文件被读取。要使设置的环境变量立即生效,使用source命令,例如:复制代码代码如下:复制代码代码如下:sudo ln -sf ~/android/sdk/tools/* /usr/bin/.这样就可以直接执行draw9path之类的命令了

10月19日消息,Canonical今天正式开始面向全球范围内的测试人员放出Ubuntu 15.10 RC(Wily Werewolf),也就是最终候选版镜像下载。其目的就是为了找出最后可能存在的bug,从而为本月22日即将发布的正式版做好准备。• Ubuntu 15.10 RC最终候选版及衍生版安装镜像下载:点击此处>>Ubuntu 15.10在开发上已经处于最终冻结状态,不出现严重bug的话,将不会再对其进行任何更新和调整。此外,Ubuntu 15.10的各种衍生版本,例如Ubuntu、Kubuntu、Lubuntu、Ubuntu Core、Ubuntu GNOME、Ubuntu Kylin、Ubuntu MATE、Ubuntu Server、Ubuntu Studio以及Xubuntu也都开始提供测试版下载。从Canonical官方的ISO镜像追踪页面上来看,Lubuntu以及Xubuntu两个版本所存在的bug最多,其中的部分问题甚至会影响到用户的正常安装,因此暂时不推荐大家尝鲜这些版本。假如你感兴趣的话,现在就可以到Ubuntu的ISO Tracker上下载这些版本的镜像,正式版预计将于10月22日正式发布。相关推荐:在Ubuntu系统下直接运行ISO文件的方法详解Ubuntu 15.10系统10月22日发布 采用Linux Kernel 4.2内核

惠普暗影精灵6新手教程(掌握惠普暗影精灵6,打造专业级游戏体验)

Nexus 是Maven仓库管理器,我们可以在本地架设一个Maven私有仓库服务器,在代理远程仓库的同时维护本地仓库,以节省带宽和时间,Nexus就可以满足这样的需要。1、本经验是基于:系统环境:ubuntu12.04nexus版本:nexus-2.10.0-02maven版本:apache-maven-3.2.1jdk版本:jdk1.7.0_402、把下载下来的压缩包解压到一个文件夹中3、解压缩后 进入 nexus-2.10.0-02-bundle/nexus-2.10.0-02/bin/jsw/conf4、修改wrapper.conf文件 修改 wrapper.java.command=jdk所在目录/bin/java (一定要带java,网上很多教程都没带java)5、然后进入 cd nexus-2.10.0-02-bundle/nexus-2.10.0-02/bin 目录 执行 ./nuxus start 就可以启动nuxus啦nexus的操作:nexus { console | start | stop | restart | status | dump }6、在地址栏里面输入:http://localhost:8081/nexus注意:步骤4很重要,假如显示没有权限,请添加权限!请配置好环境变量,jdk,maven都需要配置好!谢谢阅读,希望能帮到大家,请继续关注脚本之家,我们会努力分享更多优秀的文章。

解决电脑错误141的有效方法(快速修复电脑错误141,避免数据丢失)

安装和基本用法常用插件:1.ctags安装ctags可以建立源码树的索引,使程序员在编程时能迅速定位函数、变量、宏定义等位置去查看原形复制代码代码如下:sudo apt-get install ctags那么在终端进入到该目录后,输入命令ctags -R *,你会发现多了一个tags文件,这个就是索引文件2.taglist插件想必用过Source Insight的人都记得这样一个功能:SI能够把当前文件中的宏、全局变量、函数等tag显示在Symbol窗口,用鼠标点上述tag,就跳到该tag定义的位置;可以按字母序、该tag所属的类或scope,以及该tag在文件中出现的位置进行排序;假如切换到另外一个文件,Symbol窗口更新显示这个文件中的tag。在vim中的taglist插件所实现的就是上述类似的功能,有些功能比SI弱,有些功能比SI更强。而且,taglist插件还在不断完善中!要使用taglist plugin,必须满足:打开vim的文件类型自动检测功能:filetype on 系统中装了Exuberant ctags工具,并且taglist plugin能够找到此工具(因为taglist需要调用它来生成tag文件)你的vim支持system()调用复制代码代码如下:复制代码代码如下:复制代码代码如下:taglist/doc:taglist/plugin:复制代码代码如下:cd /usr/share/vim/vim71/doc启动vim,命令模式下输入:helptags . (.不可少,表示当前目录。也可以在任意目录中启动vim,只要指定:helptags usr/share/vim/vim71/doc/)生成帮助标签后,就可以使用下面的命令查看taglist帮助:help taglist.txt现在可以vim中试着打开taglist,输入命令:TlistToggle 来打开和关闭taglist窗口。(命令可以简写为Tlist)命令使用介绍Ctrl+] :在函数调用的地方跳转到函数定义的地方Ctrl+t:返回到上一个查找的地方解压后的目录结构如下

热门文章

友情链接

滇ICP备2023006006号-33