/** * <p> * This is called to find out how big a view should be. The parent supplies constraint information in the width and height parameters. * </p> * * <p> * The actual measurement work of a view is performed in * {@link #onMeasure(int, int)}, called by this method. Therefore, only * {@link #onMeasure(int, int)} can and must be overridden by subclasses. * </p> * * * @param widthMeasureSpec Horizontal space requirements as imposed by the * parent * @param heightMeasureSpec Vertical space requirements as imposed by the * parent * * @see #onMeasure(int, int) */ //没舍得删这些注释 感觉重要的事情都说了 为了计算整个View树的实际大小 设置实际的高和宽 每个子View都是根据父视图和自身决定实际宽高的 在onMeasure()方法中进行实际测量.传入widthMeasureSpec和heightMeasureSpec参数来表示了父View的规格 不但传入了模式 还传入了size 而对于DecorView来说 传入的模式一般为EXACTLY模式 size对应屏幕的宽高. 所以说子View的大小是父子View共同决定的 publicfinalvoidmeasure(int widthMeasureSpec, int heightMeasureSpec) {
// measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); }
/** * <p> * Measure the view and its content to determine the measured width and the * measured height. This method is invoked by {@link #measure(int, int)} and * should be overridden by subclasses to provide accurate and efficient * measurement of their contents. * </p> * * <p> * <strong>CONTRACT:</strong> When overriding this method, you * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the * measured width and height of this view. Failure to do so will trigger an * <code>IllegalStateException</code>, thrown by * {@link #measure(int, int)}. Calling the superclass' * {@link #onMeasure(int, int)} is a valid use. * </p> * * <p> * The base class implementation of measure defaults to the background size, * unless a larger size is allowed by the MeasureSpec. Subclasses should * override {@link #onMeasure(int, int)} to provide better measurements of * their content. * </p> * * <p> * If this method is overridden, it is the subclass's responsibility to make * sure the measured height and width are at least the view's minimum height * and width ({@link #getSuggestedMinimumHeight()} and * {@link #getSuggestedMinimumWidth()}). * </p> * * @param widthMeasureSpec horizontal space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * @param heightMeasureSpec vertical space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * * @see #getMeasuredWidth() * @see #getMeasuredHeight() * @see #setMeasuredDimension(int, int) * @see #getSuggestedMinimumHeight() * @see #getSuggestedMinimumWidth() * @see android.view.View.MeasureSpec#getMode(int) * @see android.view.View.MeasureSpec#getSize(int) */ protectedvoidonMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; }
//在getChildMeasureSpec中通过父View和本身的模式共同决定当前View的size publicstaticintgetChildMeasureSpec(int spec, int padding, int childDimension) { //获取当前父View的mode和size intspecMode= MeasureSpec.getMode(spec); intspecSize= MeasureSpec.getSize(spec); //获取父View的的剩余大小 intsize= Math.max(0, specSize - padding); //定义结果变量 intresultSize=0; intresultMode=0; //根据对应的mode做处理 //通过父View和本身的模式共同决定当前View的size switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
// Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
// Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //将size和mode整合为MeasureSpec模式后返回 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
layout源码分析
View layout整体流程与measure过程基本一样
结论:
需要根据ViewGroup本身的情况讨论 LinearLayout下会更看重子View的height和width 来安排对应位置 而RelativeLayout则更加关注子View的left right top bottom值 并且优先级高于width和height 甚至在部分自定义ViewGroup中 measure可能是无用的 直接使用layout方法来设置子View的位置也可以
/** * Assign a size and position to a view and all of its * descendants * * <p>This is the second phase of the layout mechanism. * (The first is measuring). In this phase, each parent calls * layout on all of its children to position them. * This is typically done using the child measurements * that were stored in the measure pass().</p> * * <p>Derived classes should not override this method. * Derived classes with children should override * onLayout. In that method, they should * call layout on each of their children.</p> * * @param l Left position, relative to parent * @param t Top position, relative to parent * @param r Right position, relative to parent * @param b Bottom position, relative to parent */
//同样注解写的很好了 分派给他和他的所有的子视图大小和位置 @SuppressWarnings({"unchecked"}) publicvoidlayout(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; } //调用setFrame方法把参数分别赋值于 intoldL= mLeft; intoldT= mTop; intoldB= mBottom; intoldR= mRight; //判断view的位置是否发生过变化 , 确定是否对当前view重新layout booleanchanged= isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
View中 protectedvoidonLayout(boolean changed, int left, int top, int right, int bottom) { } ViewGroup中 protectedabstractvoidonLayout(boolean changed, int l, int t, int r, int b);
publicvoiddraw(Canvas canvas) { ... /* * 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 ... if (!dirtyOpaque) { drawBackground(canvas); }
// skip step 2 & 5 if possible (common case) ...
// Step 2, save the canvas' layers ... if (drawTop) { canvas.saveLayer(left, top, right, top + length, null, flags); } ...
// Step 3, draw the content if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers ... if (drawTop) { matrix.setScale(1, fadeHeight * topFadeStrength); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); p.setShader(fade); canvas.drawRect(left, top, right, top + length, p); } ...
if (drawTop) { canvas.saveLayer(left, top, right, top + length, null, flags); }
Step 3, draw the content
1 2 3
// Step 3, draw the content //对View的内容进行绘制 if (!dirtyOpaque) onDraw(canvas);
1 2 3 4 5 6 7 8 9
/** * Implement this to do your drawing. * * @param canvas the canvas on which the background will be drawn */ //onDraw也是空方法需要子类根据自身去实现相应的 protectedvoidonDraw(Canvas canvas) { }
Step 4, draw the children
1 2 3
// Step 4, draw the children //绘制其子View dispatchDraw(canvas);
1 2 3 4 5 6 7 8 9
/** * Called by draw to draw the child views. This may be overridden * by derived classes to gain control just before its children are drawn * (but after its own view has been drawn). * @param canvas the canvas on which to draw the view */ protectedvoiddispatchDraw(Canvas canvas) { //dispatchDraw同样空方法 与onDraw不同的是dispatchDraw在ViewGroup中被重写 }