View绘制流程_第1页
View绘制流程_第2页
View绘制流程_第3页
View绘制流程_第4页
View绘制流程_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

精选文库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 widthMeasureSpec, int heightMeasureSpec) setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);对于非ViewGroup的View而言,通过调用上面默认的onMeasure即可完成View的测量。setMeasuredDimension函数是一个很关键的函数,它完成了对View的成员变量mMeasuredWidth和mMeasuredHeight变量赋值。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 MeasureSpec.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(mMinHeight, mBackground.getMinimumHeight();在ViewGroup中定义了measureChildren, measureChild, measureChildWith-Margins方法来对子视图进行测量,measureChildren内部实质只是循环调用measureChild。protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) /获取子视图的LayoutParamsfinal MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();/调整MeasureSpec/通过这两个参数以及子视图本身LayoutParams来共同决定子视图的测量规格final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);/调运子View的measure方法,子View的measure中会回调子View的/onMeasure方法child.measure(childWidthMeasureSpec, childHeightMeasureSpec);该方法就是对父视图提供的measureSpec参数结合自身的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与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) /如果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. /设置child的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; 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位具体尺寸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, int 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) : 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()、getMeasuredHeight()这两对方法之间的区别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方法可被重载,ViewGroup.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 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 (!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); / Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas);private void drawBackground(Canvas canvas) /获取xml中通过android:background属性或者代码中/setBackgroundColor()、setBackgroundResource()等方法进行赋值的背景/Drawable final Drawable background = mBackground; . /根据layout过程确定的View位置来设置背景的绘制区域 if (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的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的dispatchDraw()方法,该方法内部会遍历每个子/View,然后调用drawChild()方法,我们可以看下ViewGroup的drawChild方法protected boolean drawChild(Canvas canvas, View child, long drawingTime) return child.draw(canvas, this, drawingTime);drawChild()方法调运了子View的draw()方法。所以说ViewGroup类已经为我们重写了dispatchDraw()的功能实现,我们一般不需要重写该方法,但可以重载父类函数实现具体的功能。在获取画布剪切区时会自动处理掉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 invalidate(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, / 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 non-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(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; 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; . do /循环层层上级调运,直到ViewRootImpl会返回null parent = parent.invalidateChildInParent(location, dirty); while (parent != null);最后传递到ViewRootImpl的invalidateChildInParent方法结束,所以我们看下ViewRootImpl的invalidateChildInParent方法Overridepublic ViewParent invalidateChildInParent(int location, Rect dirty) . /View调运invalidate最终层层上传到ViewRootImpl后最终触发了该方法 scheduleTraversals(); . return null;这个ViewRootImpl类的invalidateChildInParent方法直接返回了null,结束了那个do while循环。scheduleTraversals会通过Handler的Runnable发送一个异步消息,调运doTraversal方法,然后最终调用performTraversals()执行重绘。所以说View调运invalidate方法的实质是层层上传到父级,直到传递到ViewRootImpl后触发了scheduleTraversals方法,然后整个View树开始重新按照上面分析的View绘制流程进行重绘任务。View的postInvalidate方法源码分析invalidate方法只能在UI Thread中执行,其他线程中需要使用postInvalidate方法public void postInvalidate() postInvalidateDelayed(0);public void postInvalidateDelayed(long delayMilliseconds) final AttachInfo attachInfo = mAttachInfo; /核心,实质就是调运了ViewRootImpl.dispatchInvalidateDelayed方法 if (attachInfo != null) attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); public void dispatchInvalidateDelayed(View view, long delayMilliseconds) Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds);调运的ViewRootImpl类的dispatchInvalidateDelayed方法, 通过ViewRootImpl类的Handler发送了一条MSG_INVALIDATE消息, 继续追踪这条消息的处理可以发现:public void handleMessage(Message msg) . switch (msg.what) case MSG_INVALIDATE: (View)msg.obj).invalidate(); break; . .invalidate系列方法请求重绘View树(也就是draw方法),如果View大小没有发生变化就不会调用layout过程,并且只绘制那些“需要重绘的”View,也就是哪个View(View只绘

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论