控件系统大纲

1.WindowManager

WindowManager的架构;

WinowManager与WindowManagerImpl的关系;

WindowManagerGlobal中的三要素以及工作流程;

2.ViewRootImpl

ViewRootImpl的作用;

构造函数中的初始化工作;

setView()方法的作用以及工作流程;

3.requsetLayout方法的作用;

3.performTraversals()的工作阶段

预测量:

MeasureSpec的作用;

第一次遍历和非第一次遍历时,测量大小是应用可用的最大尺寸;

View.post()方法的实现原理;

预测量是否进行的条件判断判断,悬浮窗口的测量大小确定以及measureHierarchy方法的调用;

measureHierarchy中针对于虚浮窗口的测量协商;

测量原理:

performMeasure()的作用;

mView.measure的实现:

测量条件的判断,onMeasure()的结果由setMeasureDimension()保存,将PFLAG_LAYOUT_REQUIRED标记加入mPrivateFlags;

测量结果的获取以及对获取结果的解析;

onMeasure算法的实现原则;

确定是否需要改变窗口尺寸:windowSizeMayChange为true,layoutRequest为true,测量结果与ViewRootImpl中所保存的当前尺寸有差异或悬浮窗口的测量结果与窗口的最新尺寸有差异;

收集LayoutParams的改动;

 

布局窗口:

relayoutWindow为核心,并根据布局结果进行相应的处理,当布局结果使得窗口尺寸发生改变时,最终测量阶段会被执行 ;

布局窗口的4个条件,满足其一即可,mFirst,windowShouldResize,insetsChanged,params!=null;

布局窗口前的两个准备工作;

布局窗口开始:relayoutWindow方法,输入和输出;

布局窗口后的处理--Insets:如果ContentInsets发生改变,使用过渡动画,调用view.fitSystemWindow()方法保存padding

布局窗口后的处理--更新尺寸;

 

最终测量:

进行预测量的条件:最新窗口的尺寸不符合预测量的结果;ContentInsets发生改变;

最终测量的参数是窗口的最新尺寸;

调用performMeasure();

 

布局控件树:

分两部分:

通过performLayout()方法进行控件树的布局

计算窗口的透明区域,并设置给WMS;

控件树布局使用了performLayout()-->mView.layout();-

layout():setFrame设置mLeft,mTop,mRight和mBottom;onLayout执行的判断条件;清除PFLAG_LAYOUT_REQUIRED标签;通知每一个对对此控件的布局变化感兴趣的监听者;清除PFLAG_FORCE_LAYOUT标签;

 

绘制:

绘制进行的判断条件:窗口处于动画,view不可见,在本次遍历中获取了一块Surface;

理解Canvas,绘图工具类,它绘图指令和辅助指令,它的绘制目标是一个建立在Surface上的位图Bitmap;

通过Surface.lockCanvas方法获取一个Canvas时会在Surface内存创建一个Bitmap,通过Canvas绘制的内容,就会显示在这个Bitmap上,并直接反应到Surface中;

Canvas的坐标系变换的意义;

View.invalidate和脏区域:为保证绘制效率,控件树仅对需要重绘的区域进行绘制;PFLAG_DIRTY或PFLAG_DIRTY_OPAQUE,根据这两个标记来判断是否为控件绘制背景,提升效率;

开始绘制:

performDraw()的工作流程:

draw(),有个参数,是否完整绘制;

ViewRootImpl需要保证某个关键的控件时可见的,如输入法弹出时,计算一个相对滚动量,使得整个控件树的绘制位置产生偏移从而使控件露出来,计算mView在垂直方向上的滚动量,为防止滚动突兀,使用了scroller;

硬件加速绘制与软件绘制的判断;

软件绘制:drawSoftware()

根据脏区域获取canvas,清空脏区域,添加View.PFLAG_DRAWN标签,canvas进行第一次变换,这次变换的目的,是为了让坐标新按照之前所计算的滚动量进行相应的滚动;

绘制:mView.draw(canvas);

drawSoftware()有四个步骤;获取canvas,对canvas进行变化实现滚动效果,通过mView.draw将根控件绘制在Canvas上,最后显示绘制的内容surface.unlockCanvasAndPost(canvas);

mView.draw(canvas)有如下几步:

绘制背景;调用onDraw()绘制自身;调用dispatchDraw()绘制子控件;绘制滚动条;

dispatchDraw():

设置裁剪区域;遍历所有子控件,有两种不同的绘制顺序,并drawChild(canvas, transientChild, drawingTime);

对getChildDrawingOrder方法的理解,影响了绘制顺序,以及事件的派发;

drawChild-->child.draw(canvas, this, drawingTime);

该方法有如下流程:

坐标系变换:控件位置,控件动画所产生的矩阵,控件自身变换的矩阵,控件内容的滚动量

 

当一个输入事件被派发给ViewRootImpl窗口时,Looper会被唤醒,并触发InputEventReceiver.onInputEvent方法,控件树的输入事件派发便起源于这一回调;

触摸模式和焦点;

触摸模式分为按键输入和触摸输入;

按键模式下,最重要的是焦点控件的查找;而触摸模式下,不需要任何一个菜单项处于焦点状态;

在非触摸模式下,文本框,按钮,菜单项都可以获得焦点,并且可以通过方向键使得焦点在这些控件之间游走;进入触摸模式后,某些控件如菜单项,按钮将不再可以保持或获取焦点,而文本框则仍然可以保持或获取焦点;

如何进入触摸模式:用户在窗口上点击;

如果退出触摸模式:用户按下了方向键,通过键盘按下了子母建,开发者执行了View.requestFocusFromTouch();

控件焦点影响了按键事件的派发,控件焦点还影响了控件的表现形式;

获取焦点的条件,view.requestFocus;

按键事件是基于焦点的派发,而触摸事件是基于位置的派发;

InputEventReceiver是输入系统的派发终点,onIuputEvent调用流程:

将InputEvent对应的InputEventReceiver封装成QueuedInputEvent;

将新建的QueuedInputEvent追加到链表中,ViewRootImpl会随着链表从头到尾地逐个处理输入事件;

doProcessInputEvents();

doProcessInputEvents()-->使用for循环,遍历全部的输入事件,逐个处理;处理输入事件deliverInputEvent;

deliverInputEvent的处理流程:

开始判断输入事件的类型,分别为处理按键事件,处理输入事件,处理轨迹球事件,处理其他事件。

不管怎么样,最终都会调用finishInputEvent,向InputDispatcher发送输入事件处理完毕的反馈;

 

按键事件的派发:

首先将事件尝试派发给输入法,派给输入法有三个条件,mView不为null,mAdded为true,QueuedInputEvent中没QueuedInputEvent.FLAG_DELIVER_POST_IME这个标签;首先是(mView. dispatchKeyEventPreIme( event)),如果为true,结束派发工作;将按键事件派发给输入法,同时中止此次事件的派发过程;将事件派发给控件树deliverKeyEventPostIme( q);

按键事件的初次派发:

mView. dispatchKeyEventPreIme( event),在view中是直接返回false,在ViewGroup中,如果ViewGroup是焦点的拥有者,有VireGroup来消费,倘若不是则把 dispatchKeyEventPreIme传递给拥有焦点的控件;

可以重写dispatchKeyEventPreIme方法来拦截特定的按键事件进行处理并阻止子控件获得它;

输入法对按键事件的处理:倘若没有控件处理这一事件,那么事件会派发给输入法;为什么要派发给输入法?按键事件将会派发给处于焦点状态的窗口,而输入法所在的窗口无法获得焦点,因此在默认情况下,输入法窗口是无法获取按键事件的,但输入法又确实有处理按键事件的需求,如back键关闭,等输入法对此时不感兴趣后,再将事件传递给控件树;派给输入法的条件是mLastWasImTarget 成员 为 true,即本窗口可能是输入法的输入目标,输入法完成事件处理之后,无论是否消费,都会通过回调InputMethodCallback.finishedEvent回调将其处理结果通知给ViewRootImpl,后者会通过handleImeFinishedEvent方法来继续对事件的派发工作;在handleImeFinishedEvent中,如果输入法消费了该操作,便中止派发,否则,将输入事件派发给控件树,dispatchKeyEvent方法,如果dispatchKeyEvent没有消费,便将者事件派发给mFallbackEventHandler;如果mFallbackEventHandler有没有消费,开始处理方向键的按下事件,用于使焦点在控件之间游走;

优先级关系:

TouchMode:如果导致了触摸模式的退出,此事件会被消费;

控件树中的view;

mFallbackEventHandler;

焦点游走;

关于dispatchKeyEvent:

首先由onKeyListener去消费,如果为true,则直接返回true;

通过event.dispatch方法将事件发送给view的指定回调,如onKeyDown,onkeyUp等;

 

触摸事件:dispatchPointerEvent

如果这是一个按下事件,将会进入触摸模式;

ViewRootImpl所收到的触摸事件位于窗口的坐标系下,将其    派发给根控件时需要将其坐标转换到根控件下;

将触摸事件派发给控件树,调用dispatchTouchEvent方法;

MotionEvent与触摸事件的序列,主要是多点触摸;

MotionEvent的拆分;

转载于:https://my.oschina.net/u/3491256/blog/911409

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值