很多时候我们在自定义 View 的需要做动画的时候,我们可以依赖属性动画的回调周期性修改 自定义的属性值,然后调用 invalidate 方法实现。
不过我还见过一个比较野的路子,它在 onDraw 里面直接修改属性,然后调用 invalidate() 方法。
运行起来好像也没问题。
那么问题来了:
- 在 onDraw 里面调用 修改绘制相关属性(例如画圆,修改半径) invalidate() ,这种与属性动画的回调调用 invalidate()源码分析有什么区别?
- 在 onDraw 里面调用 invalidate() 会存在什么问题?
更多问答 >>
-
每日一问 | mipmap vs drawable,傻傻分不清楚?
2021-03-30 21:14 -
2021-04-08 00:25
-
每日一问 | 听说你做过内存优化 之 Bitmap内存占用到底在哪?
2021-04-19 23:40 -
2021-05-06 00:16
-
每日一问 | view.requestLayout如果在灭屏或者切home之后调用会怎么样?
2021-05-06 00:16 -
每日一问 | 在做性能优化的时候,常常看到 Thread(Cpu) Time,Wall clock Time?
2021-03-15 00:43 -
2021-03-18 23:20
-
每日一问 | 今天还探索一个 View 的方法 hasOverlappingRendering()
2021-02-21 20:16 -
每日一问 | 类要先加载、链接、初始化才能实例化,有特殊Case吗?
2021-02-21 20:15 -
2021-01-31 16:58
在View.
了解ValueAnimator的同学会知道,它播放动画的实现原理并不是直接使用线程来不断计算并回调AnimatorUpdateListener,而是。。。来写代码测试下就知道了:onDraw
方法里调用View.invalidate
和在ValueAnimator.AnimatorUpdateListener中调用View.invalidate
,有区别吗?代码很简单,随便创建一个ValueAnimator然后在它的UpdateListener里面去抛出一个异常。
看看堆栈信息:看标记1处,来到了Choreographer.
接着看2,Choreographer.java第970行:doFrame
方法,View的绘制,各种输入/触摸事件等也是在这里开始处理的。这里把
action
强转为FrameCallback,而标记3处:可以看到它回调的正是AnimationHandler的
但是ValueAnimator怎么会跟AnimationHandler扯上关系呢?其实在我们调用mFrameCallback
。start
方法播放动画的时候,它就已经把一个Callback添加到AnimationHandler里面去了:这个Callback正是ValueAnimator自身。
那它最终会被传到哪里呢?代码套那么多层就不全贴了,它最终会通过Choreographer.postFrameCallback
方法:传到了Choreographer中。
可以看到它最后调用的是postCallbackDelayedInternal
方法,记住!这个很重要!好,回到主题。
通过刚刚一段分析,可以知道,ValueAnimator.AnimatorUpdateListener,是在Choreographer.doFrame
回调时才回调的。也就是说,ValueAnimator开始后,AnimatorUpdateListener会在每一次屏幕刷新的时候回调!还有一个区别就是,动画进度计算方式不同,ValueAnimator是根据记录的开始时间来计算的,所以它不会受到Activity生命周期或其他因素影响。而直接在onDraw
里回调的就不同了,如果动画在播放过程中Activity Stopped了,onDraw
就会暂停回调,那么下一次的invalidate
时间,也就无从确定了。不过,可能刚好有这样的需求,需要在Activity不可见时暂停动画呢?在View.
看了@xujiafeng同学的回答,他说这样做的话,IdleHandler不会被回调。公众号文章链接在这里:https://mp.weixin.qq.com/s/dh_71i8J5ShpgxgWN5SPEwemmmm,其实我觉得这不应该是一个问题,因为Handler的机制就是这样的啊,MessageQueue还有事情没处理完,肯定不会告诉你说它有空啦。等动画播放完毕,IdleHandler还是会正常回调的。不过你说是要无限循环播放的话,让MessageQueue一直忙碌,导致IdleHandler一直没能被回调的话,那确实是个问题,就拿常见的场景来说:每日一问 | Activity 调用了finish()方法会立即调用onDestory()吗? ,Activity的Destory也是依赖IdleHandler来完成的(虽然有超时机制)。(以后会跟大家一起debug AMS来详细分析这个问题)如果真的有这样的需求,除了改用ValueAnimator之外,就没其他方法了吗?肯定有啦,你想想ScrollView、RecyclerView、ViewPager等等这些View的惯性滚动动画效果是怎么做的?它们其实是通过一个叫onDraw
中直接调用invalidate
方法会有什么问题?postInvalidateOnAnimation
的方法来invalidate
的,关于这个方法,我记得在前面好几个回答都提到过了。来看下它原理是怎么样的吧:。。。。。。。
我要睡觉了,长话短说,它最终是调用Choreographer.
postCallback
方法来把一个会调用View.invalidate
的Runnable传进去:!!!!!看到了没?!它最终调用的是
这就说明了,使用View.postCallbackDelayedInternal
方法!还记得刚刚分析ValueAnimator的时候,叫记住的那个方法吗?就是它啊!postInvalidateOnAnimation
方法,跟在ValueAnimator.AnimatorUpdateListener中调用invalidate
,效果是一样的!睡觉了,感兴趣的同学最好自己动手去翻一下源码,这样理解很更深刻些!
小缘每次都能覆盖到我想到的东西,比如系统以来idlehandler,以及postInvalidateOnAnimation方法...
哈哈哈哈哈
小缘名字改的我都不认识了。。
我最近在看回家的诱惑这部电视剧,快看完了~
有点不明白:对于IdleHandler的影响,当View可见时,在AnimatorUpdateListener中调用invalidate和在onDraw中调用invalidate不是一样?因为他们都会 ...查看更多
有点不明白:对于IdleHandler的影响,当View可见时,在AnimatorUpdateListener中调用invalidate和在onDraw中调用invalidate不是一样?因为他们都会调用ViewRootImpl.scheduleTraversals,从而加上同步屏障。
emmmm,其实一开始我也有过这样的疑惑。今晚我回去给你画个图,你一看就明白了
缘大佬真的强
大佬别这样
@opLW
同样是调用invalidate
方法,为什么在AnimatorUpdateListener.onAnimationUpdate
里面调用,就不会阻止IdleHandler回调呢?看图就明白了,这是在onDraw
里调用invalidate
的流程(点击查看大图):来看下在AnimatorUpdateListener中调用
invalidate
的流程(点击查看大图):onAnimationUpdate
方法是每次屏幕刷新时才回调的,也就是大概16ms左右,在这16ms的间隔内,Looper可能已经把MQ里剩下的msg都取出来了,所以如果在AnimatorUpdateListener里调用invalidate
的话,会看到这样的log:睡觉
明白了,感谢大佬!!onDraw调用invalidate的时机是在当前帧结束时,此时插入的屏障会导致接下来16ms内,looper做不了idle;ValueAnimator回调中调用invalidat ...查看更多
明白了,感谢大佬!!onDraw调用invalidate的时机是在当前帧结束时,此时插入的屏障会导致接下来16ms内,looper做不了idle;ValueAnimator回调中调用invalidate是在当前帧开始时,此时插入的屏障会在当前帧结束doTraversal时移除,不会影响接下来的16ms。
没明白。。invalidate不是会向Choreographer post一个traversal类型的callback,等下一帧来时执行onDraw。为什么在onAnimationUpdate中inv ...查看更多
没明白。。invalidate不是会向Choreographer post一个traversal类型的callback,等下一帧来时执行onDraw。为什么在onAnimationUpdate中invalidate能直接执行onDraw啊?
太蠢了,为啥我会觉得invalidate一定会在下一帧执行哈哈哈。。。
在ValueAniamtor中调invalidate时,mTraversalScheduled为true是不会发送另一个同步屏障的吧。mTraversalScheduled这个变量只有在进入doTra ...查看更多
在ValueAniamtor中调invalidate时,mTraversalScheduled为true是不会发送另一个同步屏障的吧。mTraversalScheduled这个变量只有在进入doTravelsal中才置为false,而doTravelsal的回调类型是Choreographer.CALLBACK_TRAVELSAL,ValueAnimator更新的回调类型是Choreographer.CALLBACK_ANIMATION,是先于CALLBACK_TRAVELSAL回调执行
onAnimationUpdate中的invalidate是标识了当前界面是脏数据,仍然符合绘制流程判断,只是不会发送同步屏障和绘制回调处理
onDraw 里面调用 invalidate() 会导致循环调用onDraw,动画回调不会
啊。这。。。
idlehandler不会被执行,公众号推过
无限循环?