Android视图绘制和事件分发相关
绘制原理
Activity -包含-> PhoneWindow -继承于-> Window
Activity -setContentView()-> 将View设置到PhoneWindow上
PhoneWindow -包含-> DecorView(布局容器)
View -持有-> mAttachInfo(与WindowManagerService(WMS)链接) -持有-> ViewRootImpl
ViewRootImpl -有内部类-> W -继承于-> IWindow.Stub(实则是一个Binder,用于接收WMS传递的消息)
ViewRootImpl -有内部类-> ViewRootHandler -继承于-> Handler(用于接收W类的异步消息)
WindowManagerService(负责窗口的创建、显示) <-直接交互-> WindowManger <-直接交互-> ViewRootImpl
View重绘
ViewRootHandler -handleMessage(Message)-> 接收消息 -scheduleTraversals()-> 调用mTraversalRunnable刷新遍历View树
mTraversalRunnable -执行-> performTraversals():
- 判断需要重新计算试图大小 -执行-> performMeasure()
- 判断需要重新计算视图位置 -执行-> performLayout()
- 判断需要重新绘制 -执行-> performDraw()}
View重绘原因:视图本身内部状态(enable,pressed等)变化;View内部添加或者删除了View;View本身的大小和可见性发生了变化
View绘制流程
performTraversals() -> performMeasure() -> performLayout() -> performDraw()
Android屏幕绘制
performDraw() -> draw() -> drawSoftware() -> 通过Suface.lockCanvas()拿到Canvas绘图
双缓冲技术
显示器刷新时,视频控制器按照垂直同步信号从帧缓冲区取帧数据传递给显示器显示,双缓冲区则在数据刷新时将一个缓冲区中的数据直接替换另一个缓冲区
View事件分发——递归
传递过程中,当前层级执行child.dispatchTouchEvent:
-
如果child是ViewGroup,执行ViewGroup重写的dispatchTouchEvent方法,判断是否在当前层级拦截当前事件或传递给下一级
-
如果child是没有child的View或ViewGroup,执行View实现的super.dispatchTouchEvent方法,判断:
- 如果enabled并实现了onTouchListener,且onTouch()返回true,那么直接返回结果
- 否则执行onTouchEvent(),如果clickable并且实现了onClickListener或onLongClickListener,那么执行 onClick()或onLongClick()
- ViewGroup可以在当前层级设置onInterceptTouchEvent()方法返回true来拦截事件的下发
- child也可以通过getParent.requestDisallowInterceptTouchEvent()方法来阻止上一级的下发拦截(差异化地配置阈值来确保 child 执行该方法优先于父容器onInterceptTouchEvent()返回true)
- 拦截 != 消费,唯有当前层级的super.dispatchTouchEvent()返回了true,才被认定为消费