2020年2月22.距离新年已通过去了大半个月了,依旧的出不了门,依旧的躲在家里每日三餐,依旧的在家办公,也不知道下周会不会复工,再次汇总手中各类保存整理的笔记git
相关内容后续GitHub更新,想冲击金三银四的小伙伴能够找找看看,欢迎star
(顺手留下GitHub连接,须要获取相关面试等内容的能够本身去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)github
View的绘制是从上往下一层层迭代下来的。DecorView
-->ViewGroup
(--- >ViewGroup
)-->View
,按照这个流程从上往下,依次measure
(测量),layout
(布 局),draw
(绘制)面试
顾名思义,就是测量每一个控件的大小。canvas
调用measure()
方法,进行一些逻辑处理,而后调用onMeasure()
方法,在其中调用 setMeasuredDimension()
设定View的宽高信息,完成View的测量操做。框架
public final void measure(int widthMeasureSpec, int heightMeasur eSpec) { }
measure()
方法中,传入了两个参数 widthMeasureSpec, heightMeasureSpec
表示 View的宽高的一些信息。ide
protected void onMeasure(int widthMeasureSpec, int heightMeasure Spec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumW idth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heig htMeasureSpec)); }
由上述流程来看Measure
流程很简单,关键点是在于widthMeasureSpec
, heightMeasureSpec
这两个参数信息怎么得到? 布局
若是有了widthMeasureSpec
, heightMeasureSpec
,经过必定的处理(能够重写,自 定义处理步骤),从中获取View的宽/高,调用setMeasuredDimension()
方法,指定 View的宽高,完成测量工做。优化
MeasureSpec
的肯定先介绍下什么是MeasureSpec
?MeasureSpec
由两部分组成,一部分是测量模式,另外一部分是测量的尺寸大
小。 其中,Mode模式共分为三类spa
UNSPECIFIED
: 不对View进行任何限制,要多大给多大,通常用于系统内部3d
EXACTLY
: 对应LayoutParams
中的match_parent
和具体数值这两种模式。检测到 View所须要的精确大小,这时候View的最终大小就是SpecSize
所指定的值,
AT_MOST
: 对应LayoutParams
中的wrap_content
。View的大小不能大于父容器 的大小。
那么MeasureSpec
又是如何肯定的?
对于DecorView
,其肯定是经过屏幕的大小,和自身的布局参数LayoutParams
。
这部分很简单,根据LayoutParams
的布局格式(match_parent
,wrap_content
或 指定大小),将自身大小,和屏幕大小相比,设置一个不超过屏幕大小的宽高,以 及对应模式。 对于其余View(包括ViewGroup
),其肯定是经过父布局的MeasureSpec
和自身的 布局参数LayoutParams
。 这部分比较复杂。如下列图表表示不一样的状况:
当子View的LayoutParams
的布局格式是wrap_content
,能够看到子View的大小 是父View的剩余尺寸,和设置成match_parent
时,子View的大小没有区别。为了 显示区别,通常在自定义View时,须要重写onMeasure
方法,处理wrap_content
时的状况,进行特别指定。
从这里看出MeasureSpec
的指定也是从顶层布局开始一层层往下去,父布局影响 子布局。
可能关于MeasureSpec
如何肯定View大小还有些模糊,篇幅有限,没详细具体展开介绍
View的测量流程:
测量完View大小后,就须要将View布局在Window中,View的布局主要经过肯定上 下左右四个点来肯定的。
其中布局也是自上而下,不一样的是ViewGroup
先在layout()
中肯定本身的布局,然 后在onLayout()
方法中再调用子View的layout()
方法,让子View布局。在Measure 过程当中,ViewGroup
通常是先测量子View的大小,而后再肯定自身的大小。
public void layout(int l, int t, int r, int b) { // 当前视图的四个顶点 int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; // setFrame() / setOpticalFrame():肯定View自身的位置 // 即初始化四个顶点的值,而后判断当前View大小和位置是否发生了变化并返回 boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); //若是视图的大小和位置发生变化,会调用onLayout() if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PF LAG_LAYOUT_REQUIRED) { // onLayout():肯定该View全部的子View在父容器的位置 onLayout(changed, l, t, r, b); ... }
上面看出经过 setFrame()
/ setOpticalFrame()
:肯定View自身的位置,经过 onLayout()
肯定子View的布局。 setOpticalFrame()
内部也是调用了 setFrame()
,因此具体看setFrame()
怎么肯定自身的位置布局。
protected boolean setFrame(int left, int top, int right, int bot tom) { ... // 经过如下赋值语句记录下了视图的位置信息,即肯定View的四个顶点 // 即肯定了视图的位置 mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBott om); }
肯定了自身的位置后,就要经过onLayout()
肯定子View的布局。onLayout()
是一个 可继承的空方法。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { }
若是当前View就是一个单一的View,那么没有子View,就不须要实现该方法。
若是当前View是一个ViewGroup
,就须要实现onLayout
方法,该方法的实现个自 定义ViewGroup
时其特性有关,必须本身实现。
由此便完成了一层层的的布局工做。 View的布局流程:
View的绘制过程遵循以下几步:
①绘制背景 background.draw(
canvas
)
②绘制本身(onDraw
)
③绘制Children(dispatchDraw
)
④绘制装饰(onDrawScrollBars
)
从源码中能够清楚地看出绘制的顺序。
public void draw(Canvas canvas) { // 全部的视图最终都是调用 View 的 draw ()绘制视图( ViewGroup 没有复写 此方法) // 在自定义View时,不该该复写该方法,而是复写 onDraw(Canvas) 方法进行绘 制。 // 若是自定义的视图确实要复写该方法,那么须要先调用 super.draw(canvas)完 成系统的绘制,而后再进行自定义的绘制。 ... int saveCount; if (!dirtyOpaque) { // 步骤1: 绘制自己View背景 drawBackground(canvas); } // 若是有必要,就保存图层(还有一个复原图层) // 优化技巧: // 当不须要绘制 Layer 时,“保存图层“和“复原图层“这两步会跳过 // 所以在绘制的时候,节省 layer 能够提升绘制效率 final int viewFlags = mViewFlags; if (!verticalEdges && !horizontalEdges) { if (!dirtyOpaque) // 步骤2:绘制自己View内容 默认为空实现, 自定义View时需 要进行复写 onDraw(canvas); ...... // 步骤3:绘制子View 默认为空实现 单一View中不须要实现,ViewG roup中已经实现该方法 dispatchDraw(canvas); ........ // 步骤4:绘制滑动条和前景色等等 onDrawScrollBars(canvas); .......... return; } ... }
不管是ViewGroup
仍是单一的View,都须要实现这套流程,不一样的是,在 ViewGroup
中,实现了 dispatchDraw()
方法,而在单一子View中不须要实现该方 法。自定义View通常要重写onDraw()
方法,在其中绘制不一样的样式。
View绘制流程:
从View的测量、布局和绘制原理来看,要实现自定义View,根据自定义View的种 类不一样,可能分别要自定义实现不一样的方法。可是这些方法不外乎:onMeasure()
方法,onLayout()
方法,onDraw()
方法。
onMeasure()
方法: 单一View,通常重写此方法,针对wrap_content
状况,规定 View默认的大小值,避免于match_parent状况一致。ViewGroup
,若不重写,就会 执行和单子View中相同逻辑,不会测量子View。通常会重写onMeasure()
方法,循 环测量子View。
onLayout()
方法: 单一View,不须要实现该方法。ViewGroup
必须实现,该方法是 个抽象方法,实现该方法,来对子View进行布局。
onDraw()
方法: 不管单一View,或者ViewGroup
都须要实现该方法,因其是个空 方法
本身整理的983页面试大全,为打算面试或者正在面试的人提供借鉴的思路
上图知识汇总的PDF相关内容后续GitHub更新,想冲击金三银四的小伙伴能够找找看看,欢迎star
(顺手留下GitHub连接,须要获取相关面试等内容的能够本身去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)