1、View绘制流程View绘制流程第一步:递归measure源码分析/final方法,子类不可重写public final void measure(int widthMeasureSpec, int heightMeasureSpec) ./回调onMeasure()方法onMeasure(widthMeasureSpec, heightMeasureSpec);这个方法的两个参数都是父View传递过来的,代表了父view的规格。他由两部分组成,高2位表示MODE,低30位表示size。/View的onMeasure默认实现方法protected void onMeasure(int widt
2、hMeasureSpec, int heightMeasureSpec) setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);对于非ViewGroup的View而言,通过调用上面默认的onMeasure即可完成View的测量。setMeasuredDimension函数是一个很关键的函数,它完成了对View的成员变量mMeasuredWidth和mMeasur
3、edHeight变量赋值。public static int getDefaultSize(int size, int measureSpec) int result = size;/通过MeasureSpec解析获取mode与sizeint specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpe
4、c.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; return result;protected int getSuggestedMinimumWidth() return (mBackground = null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth();protected int getSuggestedMinimumHeight() return (mBackground = null) ? mMinHeight : max(mMinHe
5、ight, mBackground.getMinimumHeight();在ViewGroup中定义了measureChildren, measureChild, measureChildWith-Margins方法来对子视图进行测量,measureChildren内部实质只是循环调用measureChild。protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) /获取子视
6、图的LayoutParamsfinal MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();/调整MeasureSpec/通过这两个参数以及子视图本身LayoutParams来共同决定子视图的测量规格final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.wi
7、dth);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);/调运子View的measure方法,子View的measure中会回调子View的/onMeasure方法child.measure(childWidthMeasureSpec, childHeightMeasureSpec);该方法就是对父视图提供的m
8、easureSpec参数结合自身的LayoutParams参数进行了调整,然后再来调用child.measure()方法,具体通过方法getChildMeasureSpec来进行参数调整。public static int getChildMeasureSpec(int spec, int padding, int childDimension) /获取当前Parent View的Mode和Size int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); /获取Parent size
9、与padding差值(也就是Parent剩余大小),若差值小于0直接返回0 int size = Math.max(0, specSize - padding); /定义返回值存储变量 int resultSize = 0; int resultMode = 0; /依据当前Parent的Mode进行switch分支逻辑 switch (specMode) / Parent has imposed an exact size on us /默认Root View的Mode就是EXACTLY case MeasureSpec.EXACTLY: if (childDimension = 0) /如
10、果child的layout_wOrh属性在xml或者java中给予具体大/于等于0的数值 /设置child的size为真实layout_wOrh属性值,mode为EXACTLY resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; else if (childDimension = LayoutParams.MATCH_PARENT) /如果child的layout_wOrh属性在xml或者java中给予/MATCH_PARENT / Child wants to be our size. So be it. /设置chi
11、ld的size为size,mode为EXACTLY resultSize = size; resultMode = MeasureSpec.EXACTLY; else if (childDimension = LayoutParams.WRAP_CONTENT) /如果child的layout_wOrh属性在xml或者java中给予/WRAP_CONTENT /设置child的size为size,mode为AT_MOST / Child wants to determine its own size. It cant be / bigger than us. resultSize = size
12、; resultMode = MeasureSpec.AT_MOST; break; . /其他Mode分支类似 /将mode与size通过MeasureSpec方法整合为32位整数返回 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。MeasureSpec(View的内部类)测量规格为int型,值由高2位规格模式specMode和低30位具
13、体尺寸specSize组成。其中specMode只有三种值:MeasureSpec.EXACTLY /确定模式,父View希望子View的大小是确定的,由specSize决定;MeasureSpec.AT_MOST /最多模式,父View希望子View的大小最多是specSize指定的值;MeasureSpec.UNSPECIFIED /未指定模式,父View完全依据子View的设计值来决定;View绘制流程第二步:递归layout源码分析ViewGroup的layout方法,如下:Overridepublic final void layout(int l, int t, int r, in
14、t b) . super.layout(l, t, r, b);.调运了View父类的layout方法,所以我们看下View的layout源码,如下:public void layout(int l, int t, int r, int b) /实质都是调用setFrame方法把参数分别赋值给mLeft、mTop、mRight和/mBottom这几个变量/判断View的位置是否发生过变化,以确定有没有必要对当前的View进行重新/layout boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b)
15、 : setFrame(l, t, r, b);if (changed | (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) = PFLAG_LAYOUT_REQUIRED) onLayout(changed, l, t, r, b);/ViewGroup的onLayout方法,如下:Overrideprotected abstract void onLayout(boolean changed,int l, int t, int r, int b);关于getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHe
16、ight()这两对方法之间的区别public final int getMeasuredWidth () return mMeasuredWidth & MEASURED_SIZE_MASK;public final int getMeasuredHeight() return mMeasuredHeight & MEASURED_SIZE_MASK;public final int getWidth() return mRight - mLeft;public final int getHeight() return mBottom - mTop;View.layout方法可被重载,View
17、Group.layout为final的不可重载,ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑。凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个没有父容器的View设置相关layout_XXX属性是没有任何意义的使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值View绘制流程第三步:递归draw源码分析ViewGroup没有重写View的draw方法,所以如下直接从View的draw方法开始public
18、void draw(Canvas canvas) / 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 (!dirtyOp
19、aque) 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,
20、top + length, p); / Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas);private void drawBackground(Canvas canvas) /获取xml中通过android:background属性或者代码中/setBackgroundColor()、setBackgroundResource()等方法进行赋值的背景/Drawable final Drawable background = mBackground; . /根据layout过程确定的View位置来设置背景的绘制区域 i
21、f (mBackgroundSizeChanged) background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); mBackgroundSizeChanged = false; rebuildOutline(); . /调用Drawable的draw()方法来完成背景的绘制工作 background.draw(canvas); ./View的onDraw方法, 这是一个空方法。因为每个View的内容部分是各不相同的,/所以需要由子类去实现具体逻辑。protected void onDraw(Canvas canvas) / View的
22、dispatchDraw()方法是一个空方法,如果View包含子类需要重写他,所/以我们有必要看下ViewGroup的dispatchDraw方法源码Overrideprotected void dispatchDraw(Canvas canvas) .final int childrenCount = mChildrenCount; final View children = mChildren; for (int i = 0; i = 0; i-) more |= drawChild(canvas, child, drawingTime); / ViewGroup确实重写了View的dis
23、patchDraw()方法,该方法内部会遍历每个子/View,然后调用drawChild()方法,我们可以看下ViewGroup的drawChild方法protected boolean drawChild(Canvas canvas, View child, long drawingTime) return child.draw(canvas, this, drawingTime);drawChild()方法调运了子View的draw()方法。所以说ViewGroup类已经为我们重写了dispatchDraw()的功能实现,我们一般不需要重写该方法,但可以重载父类函数实现具体的功能。在获取画
24、布剪切区时会自动处理掉padding,子View获取Canvas不用关注这些逻辑,只用关心如何绘制即可。默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序。View的invalidate方法源码分析View类中的一些invalidate方法/This must be called from a UI thread. To call from a non-UI thread, / call postInvalidate()public void inva
25、lidate(Rect dirty) final int scrollX = mScrollX; final int scrollY = mScrollY; /实质还是调运invalidateInternal方法 invalidateInternal(dirty.left - scrollX, dirty.top - scrollY, dirty.right - scrollX, dirty.bottom - scrollY, true, false);/This must be called from a UI thread. To call from a non-UI thread, /
26、call postInvalidate()public void invalidate(int l, int t, int r, int b) final int scrollX = mScrollX; final int scrollY = mScrollY; /实质还是调运invalidateInternal方法 invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);/This must be called from a UI thread. To call from a no
27、n-UI thread, / call postInvalidate()public void invalidate() /invalidate的实质还是调运invalidateInternal方法 invalidate(true); /this function can be called with invalidateCache set to false to /skip that invalidation step void invalidate(boolean invalidateCache) /实质还是调运invalidateInternal方法invalidateInternal(
28、0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);/所有invalidate的最终调运方法void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) . / Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent
29、; if (p != null & ai != null & l r & t b) final Rect damage = ai.mTmpInvalRect; /设置刷新区域 damage.set(l, t, r, b); /传递调运Parent ViewGroup的invalidateChild方法 p.invalidateChild(this, damage); .View的invalidate(invalidateInternal)方法实质是将要刷新区域直接传递给了父ViewGroup的invalidateChild方法,在invalidate中,调用父View的invalidateChild,这是一个从当前向上级父View回溯的过程ViewGroup的invalidateChild方法public final void invalidateChild(View child, final Rect dirty) ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo;
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1