Layout
分析完measure过程后,layout和draw过程则相对简单一点。
在performTraversals过程中的代码片段如下:
//layout布局要求标志位
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
//--------开始执行布局操作---------
performLayout(lp, mWidth, mHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
//计算透明区域
....
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
....
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
// requestLayout() was called during layout.
// If no layout-request flags are set on the requesting views, there is no problem.
// If some requests are still pending, then we need to clear those flags and do
// a full request/measure/layout pass to handle this situation.
//layout期间仍有layout请求未处理则需要重新进行三个流程,
//完成三大流程仍未完成则等待下一帧
......
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
可以看到host.layout的调用,host即是DectorView。layout之后处理仍未消除的layout标志(重新走流程),若仍未消除标志通过requestLayout等待下一帧。所以跟踪去View#layout
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//确定frame的四个端点
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);//onLayout父元素确定子元素位置
......
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
......
}
在rootView起调layout流程时,传入host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); 就不难理解为确定DectorView的位置为屏幕原点到DectorView测量大小。而layout过程中调用的onLayout则View/ViewGroup并没有实现,由子类实现对应方法。而ViewGroup主要为布局子view位置,对应的实现分析:
- LinearLayout (vertical):确定children布局的上边界,遍历子view按顺序通过setChildFrame确定children位置。
- RelativeLayout:子view在measure过程中两次measure已经确定了view的位置,这时只需要获取LayoutParams中位置信息即可
- FrameLayout:子view根据自身测量高度、自身padding/margin和父布局padding/margin共同确定位置信息。
最后通过child.layout(Left, Top, Right, Bottom);确定了view的位置。在View#layout中setFrame将赋值四个边界的值。这是getWidth、getHeight则可以拿到正确的view的大小。而根据源码分析则可以发现getMeasuredWidth和getWidth的值是一样只是赋值的时机不同
public final int getHeight() {
return mBottom - mTop;
}
public final int getWidth() {
return mRight - mLeft;
}
但是重写layout方法还是可以实现getWidth与getMeasuredWidth对应值不一致,但是实质意义不大。
public void layout(int l, int t, int r, int b) {
super.layout(l, t, r + 50, b + 50);
}
Draw
走完layout流程一般将会进行draw流程。在ViewRootImpl中performDraw
//performTraversals
if (!cancelDraw && !newSurface) {
....
//-----开始执行绘制操作-------
performDraw();
}
private void performDraw() {
.......
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded); // ----绘制
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!surface.isValid()) {
return;
}
.....
mAttachInfo.mTreeObserver.dispatchOnDraw();
.....
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
......
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
// for instance) so we should just bail out. Locking the surface with software
// rendering at this point would lock it forever and prevent hardware renderer
// from doing its job when it comes back.
// Before we request a new frame we must however attempt to reinitiliaze the
// hardware renderer if it's in requested state. This would happen after an
// eglTerminate() for instance.
if (mAttachInfo.mThreadedRenderer != null &&
!mAttachInfo.mThreadedRenderer.isEnabled() &&
mAttachInfo.mThreadedRenderer.isRequested()) {
......
scheduleTraversals();
return;
}
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
则可以看到最终如果进行软件绘制的话则通过surface进行绘制流程。这里稍微说明下就是,每一个Surface中保存了一个Canvas对象。Surface是原始图像缓冲区(raw buffer)的一个句柄,而原始图像缓冲区是由屏幕图像合成器(screen compositor)管理的。通过操作Canvas将绘制图像保存到Canvas中的bitmap,然后通过Surface处理图像缓冲将canvas中bitmap内容绘制到屏幕。
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
try {
.....
canvas = mSurface.lockCanvas(dirty);
.....
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
....
}
try {
.....
try {
canvas.translate(-xoff, -yoff);
....
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
.......
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
......
}
.......
}
return true;
}
drawSoftware方法通过Surface.lockCanvas获取到对应canvas,操作完canvas通过surface.unlockCanvasAndPost(canvas)释放canvas。
在使用SurfaceView时我们需要自己手动调用这两个方法,而View中则是由ViewRootImpl系统类自行调用。
而从上面的代码中看到我们最终的绘制流程的调用mView.draw(canvas);其中mView即DectorView。
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
......
}
注释中说的很清楚了,绘制的过程主要分为4个步骤(本来6个步骤,一般情况下跳过2和5):
- 绘制背景
drawBackground(canvas) -> background.draw(canvas);
- 绘制自己
onDraw(canvas);
- 绘制子View
dispatchDraw(canvas);
,该方法在VIewGroup中有实现,即遍历调用子view的draw方法 - 绘制前景(前景、scrollbars等等)
onDrawForeground(canvas);
view中有个特殊的方法setWillNotDraw
。View不需要绘制时可以通过setWillNotDraw(true)优化绘制流程。ViewGroup默认开启这个标志位,所以ViewGroup需要绘制功能需要将这个标志位置false。
/**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
* Typically, if you override {@link #onDraw(android.graphics.Canvas)}
* you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
dispatchDraw在ViewGroup中的实现:
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
.....
int clipSaveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
//是否禁止绘制到padding区域
if (clipToPadding) {
clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
}
.....
// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// Go backwards -- we may delete as animations finish
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
....
if (clipToPadding) {
canvas.restoreToCount(clipSaveCount);
}
// mGroupFlags might have been updated by drawChild()
flags = mGroupFlags;
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
invalidate(true);
}
......
}
LinearLayout
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
// Where right end of child should go
final int width = right - left;
int childRight = width - mPaddingRight;
// Space available for child
int childSpace = width - paddingLeft - mPaddingRight;
final int count = getVirtualChildCount();
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
switch (majorGravity) {//根据gravity,计算children的上边界
......
}
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
//根据水平方向的children的gravity确定children左边界
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
.....
}
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
childTop += lp.topMargin;
//设置children边界
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
i += getChildrenSkipCount(child, i);
}
}
}
private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}
RelativeLayout
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// The layout has actually already been performed and the positions
// cached. Apply the cached values to the children.
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
RelativeLayout.LayoutParams st =
(RelativeLayout.LayoutParams) child.getLayoutParams();
child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
}
}
}
FrameLayout
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
......
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
//根据水平Gravity及padding、margin确定child的left位置
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
.....
}
//根据垂直Gravity及padding、margin确定child的Top位置
switch (verticalGravity) {
.....
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}