FrameLayout布局绘制流程解析
16lz
2021-01-26
最近在深入学习Android视图的绘制流程,从相对简单的FramgLayout开始。
onMeasure函数主要是用来计算子视图以及ViewGroup自身的大小,代码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); //如果width或者height的mode有一个不为EXACTLY那么使能measureMatchParentChildren final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; //清除mMatchParentChildren缓存,mMatchParentChildren中保存了前一次onMeasure中宽或高被设置为match_parent的view mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; //step1.遍历计算所有子View大小 for (int i = 0; i < count; i++) { final View child = getChildAt(i); //mMeasureAllChildren如果使能,那么无视可见性,强制measure所有视图,默认false if (mMeasureAllChildren || child.getVisibility() != GONE) { //measure子View,该函数属于ViewGroup measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); //记录子视图最大宽度 maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); //记录子视图最大高度 maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); //如果measureMatchParentChildren使能那么 //向mMatchParentChildren中加入所有高或宽为Match_Parent的view if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too // 考虑padding maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width // 与最小宽高比较,取较大值 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width // 与前景图片比较,获取宽高相对较大的值 final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } //step2.设置FrameLaout自身的尺寸 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); //step3.绘制宽或高为Match_Parent的view count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidthMeasureSpec; int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } }}// 考虑子视图外边距计算子视图大小protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //根据parentWidthMeasureSpec,ViewGroup剩余宽度,child的宽度重新计算child的widthMeasureSpec final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); //根据parentHeightMeasureSpec,ViewGroup剩余高度度,child的高度重新计算childHeightMeasureSpec final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); //调用measure计算大小 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}
onLayout计算子视图的位置:
@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) { // 直接调用了FrameLayout中的layoutChildren方法 layoutChildren(left, top, right, bottom, false /* no force left gravity */);}//该方法中的参数其实都是相对于父视图的void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) { final int count = getChildCount(); // 获取各个方向的padding值 final int parentLeft = getPaddingLeftWithForeground(); final int parentRight = right - left - getPaddingRightWithForeground(); final int parentTop = getPaddingTopWithForeground(); final int parentBottom = bottom - top - getPaddingBottomWithForeground(); mForegroundBoundsChanged = true; // 循环layout所有子View for (int i = 0; i < count; i++) { final View child = getChildAt(i); // 不绘制可见性为GONE的View 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; } // 获取布局方向,可能的值为rtl、ltr,分别为从右至左、从左至右布局 final int layoutDirection = getLayoutDirection(); // 结合layoutDirection和View的layout_gravity计算出真正的gravity // Gravity.getAbsoluteGravity是一个静态方法 // 源码注释: // if horizontal direction is LTR, then START will set LEFT and END will set RIGHT. // if horizontal direction is RTL, then START will set RIGHT and END will set LEFT. // 水平方向如果为从左到右,那么,Gratity为START会被设置成LEFT,END会被设置成RIGHT // 水平方向如果为从右到左,那么,Gratity为START会被设置成RIGHT,END会被设置成LEFT final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); // 获取垂直方向的Gravity final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; // 根据不同的水平比重计算View的水平坐标 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + lp.leftMargin - lp.rightMargin; break; case Gravity.RIGHT: if (!forceLeftGravity) { childLeft = parentRight - width - lp.rightMargin; break; } case Gravity.LEFT: default: childLeft = parentLeft + lp.leftMargin; } //根据垂直比重计算View的垂直坐标 switch (verticalGravity) { case Gravity.TOP: childTop = parentTop + lp.topMargin; break; case Gravity.CENTER_VERTICAL: childTop = parentTop + (parentBottom - parentTop - height) / 2 + lp.topMargin - lp.bottomMargin; break; case Gravity.BOTTOM: childTop = parentBottom - height - lp.bottomMargin; break; default: childTop = parentTop + lp.topMargin; } child.layout(childLeft, childTop, childLeft + width, childTop + height); } }}
FrameLayout并没有继承onDraw方法,而是继承了draw:
// 仅仅绘制了前景图片public void draw(Canvas canvas) { super.draw(canvas); if (mForeground != null) { final Drawable foreground = mForeground; // 根据mForegroundBoundsChanged判断是否重新设置界限 // mForegroundBoundsChanged在onLayout以及onSizeChanged方法中会被使能 if (mForegroundBoundsChanged) { mForegroundBoundsChanged = false; final Rect selfBounds = mSelfBounds; final Rect overlayBounds = mOverlayBounds; final int w = mRight-mLeft; final int h = mBottom-mTop; // 根据mForegroundInPadding判断前景是否被padding所束缚,重新设置bounds if (mForegroundInPadding) { selfBounds.set(0, 0, w, h); } else { selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); } final int layoutDirection = getLayoutDirection(); Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), foreground.getIntrinsicHeight(), selfBounds, overlayBounds, layoutDirection); foreground.setBounds(overlayBounds); } foreground.draw(canvas); }}
以上。
更多相关文章
- Android应用TranslateAnimation移动之后,利用视图的setLayoutPara
- Android绚丽加载效果视图(loading)控件
- 阅读《Android(安卓)从入门到精通》(22)——网格视图
- android api 中文 (75)―― AdapterView.OnItemClickListener
- Android(安卓)touch 事件的处理流程
- Animation动画概述和执行原理
- Android(安卓)canvas绘图基础之运动的时钟
- Android函数计算器(含源码,Android测试工程)
- Android(安卓)最常用的快速开发工具类