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

下载本文档

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

文档简介

1、 public final void measure(i nt widthMeasureSpec, int heightMeasureSpec) / 回调 onMeasure(方法 onM easure(widthMeasureSpec, heightMeasureSpec); 这个方法的两个参数都是父View传递过来的,代表了父view的规格。他由两部分组成, 咼2位表示 MODE,低30位表示size。 /View的onMeasure默认实现方法 protected void onM easure(i nt widthMeasureSpec, 精品 int heightMeasureSpe

2、c) setMeasuredDimension( getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec) ); 对于非ViewGroup的View而言,通过调用上面默认的 on Measure即可完成View的测量。 setMeasuredDime nsio n函数 是一个很关键的函数,它完成了对 View的成员变量 mMeasuredWidth 和 mMeasuredHeight变量赋值。 publi

3、c static int getDefaultSize(i nt size, int measureSpec) int result = size; /通过MeasureSpe解析获取 mode与size int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: res

4、ult = specSize; case MeasureSpec.EXACTLY: 精品 break; return result; protected int getSuggestedMinimumWidth() return (mBackground = null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth(); protected int getSuggestedMinimumHeight() return (mBackground = null) ? mMinHeight : max(mMinHeight, mBa

5、ckground.getMinimumHeight(); 在 ViewGroup 中定义了 measureChildren, measureChild, measureChildWith- measureChild。 Margi ns方法来对子视图进行测量,measureChildre n内部实质只是循环调用 protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) / 获取子

6、视图的 LayoutParams final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); / 调整 MeasureSpec /通过这两个参数以及子视图本身LayoutParams来共同决定子视图的测量规格 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUs

7、ed, 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)

8、; 该方法就是对父视图提供的measureSpe参数结合自身的LayoutParams参数进行了调整, 然后再来调用child.measure(方法,具体通过方法 getChildMeasureSpe来进行参数调整。 public static int getChildMeasureSpec(int spec, int padding, int childDimension) / 获取当前 Pare nt View 的 Mode 和 Size int specMode = MeasureSpec.getMode(spec); /获取Pare nt size与paddi ng差值(也就是 Par

9、e nt剩余大小),若差值小于0直接返 回0 int size = Math.max(0, specSize - padding); / 定义返回值存储变量 int resultSize = 0; int resultMode = 0; /依据当前Pare nt的Mode进行switch分支逻辑 switch (specMode) / Parent has imposed an exact size on us / 默认 Root View 的 Mode 就是 EXACTLY case MeasureSpec.EXACTLY: if (childDimension = 0) /如果child的

10、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.

11、/ 设置 child 的 size为 size, mode 为 EXACTLY 精品 resultMode = MeasureSpec.EXACTLY; resultSize = size; 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

12、/ bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; break; / 其他 Mode 分支类似 /将mode与size通过MeasureSpe方法整合为32位整数返回 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 用 View 的 getMeasuredWidth(和 getMeasuredHeight(方法来获取 View 测量的宽高,必 须保证这两个方法在onM easure流程之后被调用才能返回有效值。 MeasureSpec

13、( View的内部类)测量规格为 int型,值由高2位规格模式specMode和 低30位具体尺寸specSize组成。其中specMode只有三种值: 精品 MeasureSpec.EXACTLY /确定模式,父 View希望子 View的大小是确定的,由 specSize决定; MeasureSpec.AT_MOST /最多模式,父 View希望子 View的大小最多是 specSizef旨 定的值; MeasureSpec.UNSPECIFIED /未指定模式,父View完全依据子 View的设计值来决 疋; View绘制流程第二步:递归layout源码分析 ViewGroup layo

14、uts: 谨归遲更峑-Wo吨结构 Vie wG roup on Layout(); View layout; 精品 onLayoutO: ViewGroup 的 layout方法,如下: Override public final void layout(i nt l, i nt t, i nt r, int b) super.layout(l, t, r, b); 调运了 View 父类的 layout 方法,所以我们看下 View 的 layout 源码,如下: public void layout(int l, int t, int r, int b) II实质都是调用setFrame方

15、法把参数分别赋值给 mLeft、mTop、mRight和/mBottom 这几个变量 / 判断 View 的位置是否发生过变化,以确定有没有必要对当前的 View 进行重新 /layout boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed | (mPrivateFlags /ViewGroup 的 onLayout 方法,如下: Override protected abstract void onLayout(bool

16、ean changed, int l, int t, int r, int b); 关于 getWidth()、getHeight()和 getMeasuredWidth(、getMeasuredHeight(这两对方法之间的 区别 public final int getMeasuredWidth () return mMeasuredWidth public final int getMeasuredHeight() return mMeasuredHeight public final int getWidth() return mRight - mLeft; public final

17、int getHeight() return mBottom - mTop; View.layout 方法可被重载, ViewGroup.layout 为 final 的不可重载,ViewGroup.onLayout 为abstract的,子类必须重载实现自己的位置逻辑。 凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个 没有父容器的 View 设置相关 layout_XXX 属性是没有任何意义的 使用View的getWidth()和 getHeight()方法来获取View测量的宽高,必须保证这两个方 法在on Layout流程之后被调用才能返回

18、有效值 View绘制流程第三步:递归draw源码分析 ViewGroup没有重写View的draw方法,所以如下直接从 View的draw方法开始 public void draw(Ca nvas can vas) / Step 1, draw the backgro un d, if n eeded if (!dirtyOpaque) drawBackgro un d(ca nvas); / skip step 2 / Step 3, draw the content if (!dirtyOpaque) on Draw(ca nvas); / Step 4, draw the childre

19、 n dispatchDraw(ca nvas); / Step 5, draw the fade effect and restore layers if (drawTop) matrix.setScale(1, fadeHeight * topFadeStre ngth); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); p.setShader(fade); canvas.drawRect(left, top, right, top + length, p); / Step 6, draw decorations

20、(scrollbars) onDrawScrollBars(canvas); private void drawBackground(Canvas canvas) /获取xml中通过android:background属性或者代码中 /setBackgroundColor()、setBackgroundResource等方法进行赋值的背景 /Drawable final Drawable background = mBackground; /根据layout过程确定的View位置来设置背景的绘制区域 if (mBackgroundSizeChanged) background.setBound

21、s(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包含子类需要重写他,所 们

22、有必要看下ViewGroup的dispatchDraw方法源码 Override protected 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 , 然后调用drawC

23、hild()方法,我们可以看下 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

24、不用关注这些逻辑, 只用关心如何绘制即可。 默认情况下子 View 的 ViewGroup.drawChild 绘制顺序和子 View 被添加的顺序一致, 但是你也可以重载 ViewGroup.getChildDraw in gOrder()方法提供不同顺序。 iew的in validate方法源码分析 View类中的一些in validate方法 /This must be called from a UI thread. To call from a non-Ul thread, / call post In validate。 public void in validate(Rect d

25、irty) final int scrollX = mScrollX; final int scrollY = mScrollY; /实质还是调运invalidateInternal方法 in validate Intern al(dirt yeft - 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-Ul thread, / call post

26、In validate() public void in validate(i nt l, i nt t, i nt r, int b) final int scrollX = mScrollX; final int scrollY = mScrollY; /实质还是调运invalidateInternal方法 in validate Intern al(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false); /This must be called from a UI thread. To call from a n

27、on-Ul 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 方法 invalidate

28、Internal(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

29、 p = mParent; if (p != null / 设置刷新区域 / 传递调运 Parent ViewGroup 的 invalidateChild 方法 damage.set(l, t, r, b); p.invalidateChild(this, damage); View的in validate in validatel nter nal)方法实质是将要刷新区域直接传递给了父ViewGroup 的 in validateChild 方法,在 in validate 中,调用父 View 的 in validateChild,这是一个从当前 向上级父 View 回溯的过程 View

30、Group 的 invalidateChild 方法 public final void in validateChild(View child, final Rect dirty) ViewParent parent = this; final Attachlnfo attachlnfo = mAttachlnfo; do / 循环层层上级调运,直到 ViewRootlmpl 会返回 null parent = parent.invalidateChildlnParent(location, dirty); while (parent != null); 最后传递到 ViewRootlmpl

31、 的 invalidateChildlnParent 方法结束, 所以我们看下 ViewRootImpl 的 invalidateChildInParent方法 精品 public ViewParent invalidateChildlnParent(int location, Override Rect dirty) /View调运in validate最终层层上传到 ViewRootlmpI后最终触发了该方法 scheduleTraversals(); return n ull; 这个 ViewRootlmpI 类的 invalidateChildInParent方法直接返回了 null,

32、结束了那个 do while 循环。scheduleTraversal会通过 Handler 的 Runnable发送一个异步消息,调运 doTraversal 方法,然后最终调用 performTraversals(执行重绘。所以说 View调运in validate方法的实 质是层层上传到父级,直到传递到ViewRootImpl后触发了 scheduleTraversals方法,然后 整个View树开始重新按照上面分析的View绘制流程进行重绘任务。 View的post In validate方法源码分析 in validate方法只能在UI Thread中执行,其他线程中需要使用post

33、I nvalidate方法 public void post In validate() postI nvalidateDelayed(O); public void post In validateDelayed(l ong delayMillisec on ds) 精品 final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); public void dispat

34、chInvalidateDelayed(View view, long delayMilliseconds) Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); 调运的 ViewRootlmpI 类的 dispatchlnvalidateDelayec方法,通过 ViewRootlmpI 类的 Handler 发送了一条 MSG_INVALIDATE 消息 , 继续追踪这条消息的处理可以发现: public void handleMessage(Message msg) switch (msg.what) case MSG_lNVALlDATE: (View)msg.obj).invalidate(); break; invalidate 系列方法请求重绘 View 树(也就是 d

温馨提示

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

最新文档

评论

0/150

提交评论