Android之View绘制问题汇总

2023-03-06 20:01:33 浏览数 (1)

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如果有滑动嵌套情形,需要处理好滑动冲突

0 人点赞