前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android之View绘制问题汇总

Android之View绘制问题汇总

原创
作者头像
李林LiLin
修改2023-03-06 20:01:33
1.1K0
修改2023-03-06 20:01:33
举报
文章被收录于专栏:Android进阶编程

1、MeasureSpec是什么?

  • MeasureSpec是一种“测量规则”或者“测量说明书”,决定了View的测量过程
  • View的MeasureSpec会根据自身的LayoutParamse和父容器的MeasureSpec生成。
  • 最终根据View的MeasureSpec测量出View的宽/高(测量时数据并非最终宽高)

2、MeasureSpec的组成?

  • MeasureSpec代表一个32位int值,高2位是SpecMode,低30位是SpecSize
  • SpecMode是指测量模式
  • SpecSize是指在某种测量模式下的大小
  • 类MesaureSpec提供了用于SpecMode和SpecSize打包和解包的方法

3、测量模式SpecMode的类型和具体含义?

  • UNSPECIFIED:父容器不对View有任何限制,一般用于系统内部
  • EXACTLY:精准模式,View的最终大小就是SpecSize指定的值(对应于LayoutParams的match_parent和具体的数值)
  • AT_MOST:最大值模式,大小不能大于父容器指定的值SpecSize(对应于wrap_content)

4、MeasureSpec和LayoutParams的对应关系?

  • View的MeasureSpec是需要通过自身的LayoutParams和父容器的MeasureSpec一起才能决定
  • DecorView(顶级View)是例外,其本身MeasureSpec由窗口尺寸和自身LayoutParams共同决定
  • MeasureSpec一旦确定,onMeasure中就可以确定View的测量宽/高

5、如何获取View的测量宽/高?

  • 在measure完成后,可以通过getMeasuredWidth/Height()方法,就能获得View的测量宽高
  • 在一定极端情况下,系统需要多次measure,因此得到的值可能不准确,最好的办法是在onLayout方法中获得测量宽/高或者最终宽/高

6、如何在Activity启动时获得View的宽/高?

  • Activity的生命周期与View的measure不是同步运行,因此在onCreate/onStart/onResume均无法正确得到
  • 若在View没有测量好时,去获得宽高,会导致最终结果为0

7、Activity中获得View宽高的4种办法?

  • onWindowFocusChanged
    • View已经初始化完毕,可以获得宽高,Activity得到焦点和失去焦点均会调用一次(频繁onResume和onPause会导致频繁调用)
  • view.post(runnable)
    • 通过post将一个runnable投递到消息队列尾部,等到Looper调用次runnable时,View已经完成初始化
  • ViewTreeObserver
    • 使用ViewTreeObserver的接口,可以在View树状态改变或者View树内部View的可见性改变时,onGlobalLayout会被回调,能正确获取View宽/高
  • view.measure

8、Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?

  • Activity调用startActivity方法,最终会调用ActivityThread的handleLaunchActivity方法
  • handleLaunchActivity会调用performLauchActivity方法(会调用Activity的onCreate,并完成DecorView的创建)和handleResumeActivity方法
  • handleResumeActivity方法会做四件事:
    • performResumeActivity(调用activity的onResume方法)
    • getDecorView(获取DecorView)
    • getWindowManager(获取WindowManager)
    • WindowManager.addView(decor, 1)
  • WindowManager.addView(decor, 1)本质是调用WindowManagerGlobal的addView方法。其中主要做两件事:
    • 创建ViewRootImpl实例
    • root.setView(decor, ….)将DecorView作为参数添加到ViewRoot中,这样就将DecorView加载到了Window中
  • ViewRootImpl还有一个方法performTraveals方法,用于让ViewTree开始View的工作流程:其中会调用performMeasure/Layout/Draw()三个方法,分别对应于View的三大流程。

9、自定义View性能优化有哪些?

  • 避免过度绘制
    • 像素点能画一次就不要多次绘制,以及绘制看不到的背景。开发者选项里内的工具,只对xml布局有效果,看不到自定义View的过度绘制,仍然需要注意。
  • 尽量减少或简化计算
    • 不要做无用计算。尽可能的复用计算结果。
    • 应该避免在for或while循环中做计算。比如:去计算屏幕宽度等信息。
  • 避免创建大量对象造成频繁GC
    • 应该避免在for或while循环中new对象。这是减少内存占用量的有效方法。
  • 禁止或避免I/O操作
    • I/O操作对性能损耗极大,不要在自定义View中做IO操作。
  • onDraw中避免冗余代码、避免创建对象
    • onDraw中禁止new对象。如:不应该在ondraw中创建Paint对象。Paint类提供了reset方法。可以在初始化View时创建对象。
    • 要避免冗余代码,提高效率。
  • 复合View,要减少布局层级。
    • 复合控件:继承自现有的LinearLayout等ViewGroup,然后组合多个控件来实现效果。这种实现方法要注意减少布局层级,层级越高性能越差。
  • 状态和恢复和保存
    • Activity还会因为内存不足或者旋转屏幕而导致重建Activity,自定义View也要去进行自我状态的保存和读取。
    • 在onSaveInstanceState()保存状态;在onRestoreInstanceState()恢复状态
  • 开启硬件加速
  • 合理使用invalidate的参数版本。
    • 避免任何情况下调用默认参数的invalidate
    • 调用有参数的invalidate进行局部和子View刷新,能够提高性能。
  • 减少冗余代码
    • 不要使用Handler,因为已经有post系列方法,View已经有post系列方法,没有必要重复去写,可以直接使用,最终会投递到主线程的Handler中
  • 使用的线程和动画,要在onDetachedFromWindow中进行清理工作。
    • View如果有线程或者动画,需要及时停止,View的onDetachedFromWindow会在View被remove时调用,在该方法内进行终止。这样能避免内存泄露
  • 要妥善处理滑动冲突。
    • View如果有滑动嵌套情形,需要处理好滑动冲突

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、MeasureSpec是什么?
  • 2、MeasureSpec的组成?
  • 3、测量模式SpecMode的类型和具体含义?
  • 4、MeasureSpec和LayoutParams的对应关系?
  • 5、如何获取View的测量宽/高?
  • 6、如何在Activity启动时获得View的宽/高?
  • 7、Activity中获得View宽高的4种办法?
  • 8、Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?
  • 9、自定义View性能优化有哪些?
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档