范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Android关于绘制的一个小细节分享

  作者:陈小缘
  很多时候我们在自定义 View 的需要做动画的时候,我们可以依赖属性动画的回调周期性修改 自定义的属性值,然后调用   invalidate    方法实现。
  不过我还见过一个比较野的路子,它在   onDraw    里面直接修改属性,然后调用 invalidate()    方法。
  运行起来好像也没问题。
  那么问题来了:  在   onDraw    里面调用 修改绘制相关属性(例如画圆,修改半径) invalidate()    ,这种与属性动画的回调调用 invalidate()   源码分析有什么区别?在   onDraw    里面调用 invalidate()    会存在什么问题?
  在View.onDraw方法里调用View.invalidate和在ValueAnimator.AnimatorUpdateListener中调用View.invalidate,有区别吗?
  了解  ValueAnimator   的同学会知道,它播放动画的实现原理并不是直接使用线程来不断计算并回调AnimatorUpdateListener   ,而是…来写代码测试下就知道了:ValueAnimator.ofInt(1).apply {     addUpdateListener {         // 因为animatedFraction=0时是直接回调的         if (it.animatedFraction > 0) {             throw RuntimeException()         }     }     start() }
  代码很简单,随便创建一个  ValueAnimator   然后在它的UpdateListener   里面去抛出一个异常。
  看看堆栈信息:  E/AndroidRuntime: FATAL EXCEPTION: main     Process: com.wuyr.wanandroidqa, PID: 16027     java.lang.RuntimeException         at com.wuyr.wanandroidqa.activities.main.TestActivity$start$1$1.onAnimationUpdate(TestActivity.kt:112)         at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1566)         at android.animation.ValueAnimator.animateBasedOnTime(ValueAnimator.java:1357)         at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1489)         at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:146)         at android.animation.AnimationHandler.access$100(AnimationHandler.java:37)         //////////         //   3         //////////         at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:54)         //////////         //   2         //////////         at android.view.Choreographer$CallbackRecord.run(Choreographer.java:970)         at android.view.Choreographer.doCallbacks(Choreographer.java:796)         //////////         //   1         //////////         at android.view.Choreographer.doFrame(Choreographer.java:727)         at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)         at android.os.Handler.handleCallback(Handler.java:938)         at android.os.Handler.dispatchMessage(Handler.java:99)         at android.os.Looper.loop(Looper.java:223)         at android.app.ActivityThread.main(ActivityThread.java:7656)         at java.lang.reflect.Method.invoke(Native Method)         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
  看标记1处,来到了  Choreographer.doFrame   方法,View的绘制,各种输入/触摸事件等也是在这里开始处理的。
  接着看2,  Choreographer.java   第970行:private static final class CallbackRecord {     public Object action; // Runnable or FrameCallback      public void run(long frameTimeNanos) {             ......             ((FrameCallback)action).doFrame(frameTimeNanos); // 970行             ......     } }
  这里把action强转为  FrameCallback   ,而标记3处:public class AnimationHandler {     private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {         @Override         public void doFrame(long frameTimeNanos) {             doAnimationFrame(getProvider().getFrameTime()); // 54行             if (mAnimationCallbacks.size() > 0) {                 getProvider().postFrameCallback(this);             }         }     }; }
  可以看到它回调的正是  AnimationHandler   的mFrameCallback   。
  但是  ValueAnimator   怎么会跟AnimationHandler   扯上关系呢?
  其实在我们调用  start   方法播放动画的时候,它就已经把一个Callback添加到AnimationHandler   里面去了:public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {      @Override     public void start() {         start(false);     }      private void start(boolean playBackwards) {         ......          addAnimationCallback(0);          ......     }       private void addAnimationCallback(long delay) {         AnimationHandler.getInstance().addAnimationFrameCallback(this, delay);     }      /*         这里实现AnimationHandler.AnimationFrameCallback接口的方法     */     @Override     public final boolean doAnimationFrame(long frameTime) {         ......     } }
  这个Callback正是  ValueAnimator   自身。
  那它最终会被传到哪里呢?
  代码套那么多层就不全贴了,它最终会通过  Choreographer.postFrameCallback   方法:public final class Choreographer {      public void postFrameCallback(FrameCallback callback) {         postFrameCallbackDelayed(callback, 0);     }      public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {         ......         postCallbackDelayedInternal(CALLBACK_ANIMATION,                 callback, FRAME_CALLBACK_TOKEN, delayMillis);     } }
  传到了  Choreographer   中。
  可以看到它最后调用的是  postCallbackDelayedInternal   方法,记住!这个很重要!
  好,回到主题。
  通过刚刚一段分析,可以知道,  ValueAnimator.AnimatorUpdateListener   ,是在Choreographer.doFrame   回调时才回调的。也就是说,ValueAnimator   开始后,AnimatorUpdateListener   会在每一次屏幕刷新的时候回调!
  还有一个区别就是,动画进度计算方式不同,  ValueAnimator   是根据记录的开始时间来计算的,所以它不会受到Activity生命周期或其他因素影响。而直接在onDraw   里回调的就不同了,如果动画在播放过程中Activity Stopped了,onDraw   就会暂停回调,那么下一次的invalidate时间,也就无从确定了。不过,可能刚好有这样的需求,需要在Activity不可见时暂停动画呢?
  在View.onDraw中直接调用invalidate方法会有什么问题?
  看了  @xujiafeng  同学的回答,他说这样做的话,  IdleHandler   不会被回调。公众号文章在这里:Android 避坑指南:实际经历来说说IdleHandler的坑
  emmmm,其实我觉得这不应该是一个问题,因为Handler的机制就是这样的啊,  MessageQueue   还有事情没处理完,肯定不会告诉你说它有空啦。
  等动画播放完毕,  IdleHandler   还是会正常回调的。
  不过你说是要无限循环播放的话,让  MessageQueue   一直忙碌,导致IdleHandler   一直没能被回调的话,那确实是个问题,就拿常见的场景来说:每日一问 | Activity 调用了finish()方法会立即调用onDestory()吗? ,Activity的Destory   也是依赖IdleHandler   来完成的(虽然有超时机制)。(以后会跟大家一起debug AMS来详细分析这个问题)
  https://www.wanandroid.com/wenda/show/13244
  如果真的有这样的需求,除了改用  ValueAnimator   之外,就没其他方法了吗?
  肯定有啦,你想想  ScrollView   、RecyclerView   、ViewPager   等等这些View的惯性滚动动画效果是怎么做的?
  它们其实是通过一个叫  postInvalidateOnAnimation   的方法来invalidate   的,关于这个方法,我记得在前面好几个回答都提到过了。
  来看下它原理是怎么样的吧:
  长话短说,它最终是调用  Choreographer.postCallback   方法来把一个会调用View.invalidate   的Runnable   传进去:public final class Choreographer {     public void postCallback(int callbackType, Runnable action, Object token) {         postCallbackDelayed(callbackType, action, token, 0);     }      public void postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis) {         ......         postCallbackDelayedInternal(callbackType, action, token, delayMillis);     } }
  !!!!!看到了没?!它最终调用的是  postCallbackDelayedInternal   方法!还记得刚刚分析ValueAnimator   的时候,叫记住的那个方法吗?就是它啊!
  这就说明了,使用  View.postInvalidateOnAnimation   方法,跟在ValueAnimator.AnimatorUpdateListener   中调用invalidate   ,效果是一样的!
  同样是调用invalidate方法,为什么在AnimatorUpdateListener.onAnimationUpdate里面调用,就不会阻止IdleHandler回调呢?
  看图就明白了,这是在  onDraw   里调用invalidate   的流程:
  就算MQ里没有其他的msg,在每次Traversal任务即将处理完毕时又向MQ塞入了新的msg,所谓一波未平,一波又起,这样的话,  IdleHandler   肯定没机会回调了。
  来看下在  AnimatorUpdateListener   中调用invalidate   的流程:
  因为  AnimatorUpdateListener   的onAnimationUpdate   方法是每次屏幕刷新时才回调的,也就是大概16ms左右,在这16ms的间隔内,Looper可能已经把MQ里剩下的msg都取出来了,所以如果在AnimatorUpdateListener   里调用invalidate   的话,会看到这样的log:onAnimationUpdate: invoked onAnimationUpdate: invoked onDraw: invoked queueIdle: invoked onAnimationUpdate: invoked onDraw: invoked queueIdle: invoked onAnimationUpdate: invoked onDraw: invoked queueIdle: invoked onAnimationUpdate: invoked onDraw: invoked queueIdle: invoked onAnimationUpdate: invoked onDraw: invoked queueIdle: invoked最后
  在这里还分享一份由大佬亲自收录整理的学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取

魅族18Spro和苹果11价格一样,到底选择谁买合适?对于这两款手机,该如何选择的问题,首先你就要问一下自己到底有没有5G网络的需求。如果你想要购买一台5G手机,那么直接入手魅族18Spro这款手机就可以了。如果你习惯使用iOS系统且网友4780买到美版扩容黑解iPhone12,找商家理论却被拉黑今天有网友找到我,说自己好像买到了扩容机,他买的是128GB美版iPhone12,但通过整机报告发现该机是64GB。要知道4780元这个价格不低,如果是买国行二手iPhone121华为Mate9推送鸿蒙HarmonyOS2。0。0。55更新华为Mate9手机推送鸿蒙HarmonyOS2。0。0。55更新,版本号为C00E50R1P2,大小为3。85GB。华为Mate9更新页面的照片更新包1更新日志软件更新包在更新后会中物联9月电商物流农村业务量指数回升5。5个点中新经纬10月8日电据中国物流与采购联合会网站8日消息,中国物流与采购联合会(简称中物联)和京东集团联合调查的2021年9月份中国电商物流运行指数为111。2点,比上月回升1。2个除了住宿还能零关税买国际大牌,酒店业首个跨境电商平台上线不出国门,甚至无需去免税店,足不出户就能以零关税的价格买到国际大牌,或者海外爆款。近日,首旅如家推出的首免全球购平台正式上线,这也是酒店行业内的首个跨境电商平台。随着中国经济的复苏小米10S重回低价,256GB定价亲民,终于等到了注重性价比的用户都知道,今年上半年发布的产品中,红米K40Pro和真我GT的性价比极高,这两款手机在拥有骁龙888芯片120Hz高刷6400万主摄等亮点的基础上,起售价都不到280贾跃亭回应JCapitalResearch做空报告冷饭热潮,无稽之谈雷递网乐天10月8日报道JCapitalResearch(美奇金投资)入群发布了针对FaradayFuture(简称FF,纳斯达克股票代码FFIE)的做空报告。针对该做空机构的报告教育行业大裁员,1000W从业者何去何从?传言中的双减靴子终于落地,在去年还风头正劲的在线教育行业,瞬间跌入了谷底。从资本运作烧钱大战制造焦虑开始的行业泡沫破灭,谁也没有预料到这一天来的这么快,即便很多培训机构第一时间都八2457亿参数的巨量模型,意味着什么?源1。0模型参数规模为2457亿,训练采用的中文数据集达5000GB,相比GPT3模型1750亿参数量和570GB训练数据集,源1。0参数规模领先40,训练数据集规模领先近10倍。月球19。6亿年前仍存在岩浆活动!嫦娥五号月球样品研究首篇学术成果发表新华社北京10月8日电(记者胡喆)记者8日从国家航天局获悉,近期由中国地质调查局中国地质科学院地质研究所北京离子探针中心刘敦一研究员和地质所海外高级访问学者澳大利亚科廷大学亚历山大微软对Windows11提供AndroidApp兼容性的承诺沉默不语Windows11终于正式发布推送了。对于不少用户来说,新系统最诱人的地方,莫过于支持运行AndroidApp。虽然已经有不少设备系统可以做到这一点,比如华为笔记本,比如统信UOS
超九成处于僵尸状态互联网医院为何雷声大雨点小?来源科技日报孙静的母亲得了肠癌。主治大夫说,孙静特别孝顺,只要条件允许,无论检查输液还是住院,她一直陪在母亲身边。但因为工作原因,孙静每年有一半时间在中国,一半时间在美国。在美国时3色3模3向3档的机械键盘,让小姐姐办公得心应手重点说明文中所作仅为本人从一名普通消费者的角度评价产品,内容有失偏颇,期待大家本着交流的态度深入讨论。好东东就不能让小姐姐看到,话说我家里一直使用一个87键的机械键盘,LD前几天做新鲜早科技丨苹果将在韩国允许开发者使用第三方支付腾讯旗下三大产品融合打通海信发布首颗自研8KAI芯片21世纪经济报道数字经济课题组综合报道早上好,新的一天又开始了。在过去24小时内,科技行业发生了哪些有意思的事情?来跟21tech一起看看吧。巨头风向标1苹果妥协,将在韩国允许应用美国软件巨头的无奈能收割全球市场,唯独在中国赚不到钱?PSPRAEAI,对于设计动画视频制作相关从业者来说,应该非常熟悉。这些软件都出自同一家美国公司。从PC到移动互联网,再到云计算时代,尽管经历了多次转型,但它始终在数字媒体领域处于从女版乔布斯到美国贾跃亭,一个硅谷天才少女的堕落来源华尔街见闻从滴血成金的硅谷明星一步步变成臭名昭著的骗子,她的百亿骗局何以维持十年之久?我小时候和叔叔一起过暑假,我记得他多爱沙滩,我记得我多么爱他。不幸的是,有一天,他被诊断出中国这5项黑科技独步全球,连美国都羡慕不已,那它们都是什么?图为特高压输电如今中国已经成为举世瞩目的科技强国和基建大国,盘点中国这5项黑科技独步全球,连美国都羡慕不已,那它们都是什么呢?特高压输电量子计算机天宫空间站可控核聚变超级钢,这些都灵活用工是共享经济最顶级的体现所谓传统标准就业,是指用人单位与劳动者签订书面劳动合同,明确约定工作岗位工作地点工作时间工资等传统劳动关系。传统的标准就业模式要求工人按照标准工时制上下班。对于用人单位来说,很容易松下首届品牌日盛大开幕,购进口冰箱享超低价格过完元旦假期,春节已经在不远处向我们招手。为了回馈顾客,很多品牌都会在春节期间推出优惠活动,松下也不例外。最近,松下召开了首届品牌日活动,推出了很多力度较大的优惠政策。一些爆款冰箱现在的小偷喜欢偷什么?现在的小偷喜欢偷什么?说实话,现在小偷的日子没前几年那么舒服了。一是群众用惯了网上支付,不再携带和储存现金了,掏包以及入户,能弄到大把现金的可能性非常小。二是现在视频监控密度空前提中国公司拿下苹果稀土大单,每月提供50吨氧化镨钕,还是长期供应要知道,近段时间来自美国的苹果公司,可是外界关注的焦点,因为这家科技公司成为全球首个市值3万亿美元的公司。并且还被外界称为是富可敌国,那么究竟有多么富有?举个最简单的例子,苹果3万当年,那个扬言要摧毁人类的女性机器人索菲亚,如今怎么样了?21世纪,我们正处于智能时代,的确,现在是信息化高速发展的年代,智能已经走进了千家万户,更多地解放了人们的双手。也就是因为如此智能机器人出现在了人们的生活里,勘探海域的水滴机器人清