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

下载本文档

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

文档简介

1、View 绘制流程第一步:递归measure 源码分析/final方法,子类不可重写public final void measure(int widthMeasureSpec, intheightMeasureSpec) ./ 回调 onMeasure()方法onMeasure(widthMeasureSpec, heightMeasureSpec);这个方法的两个参数都是父 View 传递过来的,代表了父 view 的规格。他由两部分组成,高 2 位表示 MODE,低 30 位表示 size 。/View的 onMeasure 默认实现方法protected void onMeasure(

2、int widthMeasureSpec,int heightMeasureSpec) setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);对于非 ViewGroup的 View 而言,通过调用上面默认的onMeasure测量。 setMeasuredDimension函数是一个很关键的函数,它完成了对量 mMeasuredWidth和 mMeasuredHe

3、ight变量赋值。即可完成 ViewView 的的成员变精选文库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;

4、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) ? mMinHe

5、ight : max(mMinHeight, mBackground.getMinimumHeight();在 ViewGroup 中定义了 measureChildren, measureChild, measureChildWith-Margins 方法来对子视图进行测量, measureChildren 内部实质只是循环调用 measureChild 。protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec

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

7、ightMargin+ widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin-2精选文库+ heightUsed, lp.height);/ 调运子 View 的 measure 方法,子 View 的 measure 中会回调子 View 的/onMeasure方法child.measure(childWidthMeasure

8、Spec, childHeightMeasureSpec);该方法就是对父视图提供的measureSpec参数结合自身的LayoutParams参数进行了调整,然后再来调用child.measure()方法,具体通过方法getChildMeasureSpec来进行参数调整。public static int getChildMeasureSpec(int spec, int padding, intchildDimension) /获取当前 Parent View的 Mode 和 Sizeint specMode = MeasureSpec.getMode(spec);int specSize

9、 = MeasureSpec.getSize(spec);/获取 Parentsize与 padding差值(也就是Parent剩余大小),若差值小于0直接返回 0int 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

10、case MeasureSpec.EXACTLY:if (childDimension >= 0) /如果 child的 layout_wOrh属性在 xml 或者 java中给予具体大/ 于等于 0 的数值/设置 child的 size 为真实 layout_wOrh 属性值,mode 为 EXACTLYresultSize = childDimension;resultMode = MeasureSpec.EXACTLY; else if (childDimension = LayoutParams.MATCH_PARENT) /如果 child的 layout_wOrh属性在 xm

11、l 或者 java中给予/MATCH_PARENT/ Child wants to be our size. So be it./设置 child的 size 为 size , mode 为 EXACTLYresultSize = size;resultMode = MeasureSpec.EXACTLY; else if (childDimension = LayoutParams.WRAP_CONTENT) /如果 child的 layout_wOrh属性在 xml 或者 java中给予/WRAP_CONTENT/设置 child的 size 为 size , mode 为 AT_MOST

12、/ Child wants to determine its own size. It can't be/ bigger than us.resultSize = size;-3精选文库resultMode = MeasureSpec.AT_MOST;break;./ 其他 Mode 分支类似/ 将 mode 与 size 通过 MeasureSpec 方法整合为 32 位整数返回return MeasureSpec.makeMeasureSpec(resultSize, resultMode);·用 View 的 getMeasuredWidth()和 getMeasure

13、dHeight()方法来获取View 测量的宽高,必须保证这两个方法在onMeasure 流程之后被调用才能返回有效值。·MeasureSpec (View 的内部类)测量规格为int型,值由高 2 位规格模式specMode和低 30 位具体尺寸specSize组成。其中specMode 只有三种值:MeasureSpec.EXACTLY /确定模式,父View 希望子 View 的大小是确定的,由specSize决定;MeasureSpec.AT_MOST / 最多模式,父 View 希望子 View 的大小最多是 specSize指定的值;MeasureSpec.UNSPECI

14、FIED / 未指定模式,父 View 完全依据子 View 的设计值来决定;View 绘制流程第二步:递归layout源码分析-4精选文库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 方法把参数分别

15、赋值给 mLeft 、 mTop、 mRight 和 /mBottom 这几个变量/ 判断 View 的位置是否发生过变化,以确定有没有必要对当前的View 进行重新/layoutboolean 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)

16、;/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 getMeasuredH

17、eight() return mMeasuredHeight & MEASURED_SIZE_MASK;public final int getWidth() return mRight - mLeft;public final int getHeight() return mBottom - mTop;-5精选文库·View.layout方 法 可 被 重 载 , ViewGroup.layout为final的 不 可 重 载 ,ViewGroup.onLayout为 abstract的,子类必须重载实现自己的位置逻辑。·凡是 layout_XXX的布局属性基本都

18、针对的是包含子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 neede

19、d if (!dirtyOpaque) drawBackground(canvas);/ skip step 2 & 5 if possible (common case)/ Step 2, save the canvas' layersif (drawTop) canvas.saveLayer(left, top, right, top + length, null, flags);/ Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);/ Step 4, draw the children dispatchDra

20、w(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) onDrawScr

21、ollBars(canvas);-6精选文库private void drawBackground(Canvas canvas) / 获取 xml 中通过 android:background属性或者代码中/setBackgroundColor()、 setBackgroundResource()等方法进行赋值的背景/Drawablefinal Drawable background = mBackground;./ 根据 layout 过程确定的 View 位置来设置背景的绘制区域if (mBackgroundSizeChanged) background.setBounds(0, 0, m

22、Right - mLeft, mBottom - mTop);mBackgroundSizeChanged = false;rebuildOutline();./ 调用 Drawable 的 draw() 方法来完成背景的绘制工作background.draw(canvas);./View的 onDraw 方法 ,这是一个空方法。因为每个View 的内容部分是各不相同的,/ 所以需要由子类去实现具体逻辑。protected void onDraw(Canvas canvas) / View的 dispatchDraw()方法是一个空方法,如果View 包含子类需要重写他,所/ 以我们有必要看下

23、ViewGroup的 dispatchDraw方法源码Overrideprotected void dispatchDraw(Canvas canvas) .final int childrenCount = mChildrenCount;final View children = mChildren;for (int i = 0; i < childrenCount; i+) if (child.mViewFlags & VISIBILITY_MASK) = VISIBLE |child.getAnimation() != null) more |= drawChild(can

24、vas, child, drawingTime);/ Draw any disappearing views that have animationsif (mDisappearingChildren != null) for (int i = disappearingCount; i >= 0; i-) more |= drawChild(canvas, child, drawingTime);-7精选文库/ ViewGroup确实重写了View 的 dispatchDraw()方法,该方法内部会遍历每个子/View,然后调用drawChild()方法,我们可以看下ViewGroup的

25、 drawChild方法protected boolean drawChild(Canvas canvas, View child,long drawingTime) return child.draw(canvas, this, drawingTime);drawChild()方法调运了子View 的 draw() 方法。所以说ViewGroup类已经为我们重写了 dispatchDraw() 的功能实现,我们一般不需要重写该方法,但可以重载父类函数实现具体的功能。·在获取画布剪切区时会自动处理掉 padding ,子 View 获取 Canvas 不用关注这些逻辑,只用关心如何绘

26、制即可。·默认情况下子 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

27、 = 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(in

28、t 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 invali

29、date() -8精选文库/invalidate的实质还是调运invalidateInternal方法invalidate(true);/thisfunctioncan be calledwithinvalidateCachesettofalseto/skipthat invalidation stepvoid invalidate(boolean invalidateCache) /实质还是调运 invalidateInternal 方法 invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop,invalidateCache, true

30、);/ 所有 invalidate的最终调运方法voidinvalidateInternal(intl,intt,intr,intb,booleaninvalidateCache, 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

31、 < 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 回溯的过程Vi

32、ewGroup的 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);-9精选文库最后传递到ViewRootImp

33、l的 invalidateChildInParent方法结束,所以我们看下ViewRootImpl的 invalidateChildInParent方法Overridepublic ViewParent invalidateChildInParent(int location,Rect dirty)./View调运 invalidate最终层层上传到ViewRootImpl后最终触发了该方法scheduleTraversals();.return null;这个 ViewRootImpl类的 invalidateChildInParent方法直接返回了null,结束了那个 do while循环

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

35、blic void postInvalidate() postInvalidateDelayed(0);public void postInvalidateDelayed(long delayMilliseconds) final AttachInfo attachInfo = mAttachInfo;/ 核心,实质就是调运了 ViewRootImpl.dispatchInvalidateDelayed 方法 if (attachInfo != null) delayMilliseconds);public void dispatchInvalidateDelayed(View view,lo

36、ng delayMilliseconds) Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds);调运的 ViewRootImpl类的 dispatchInvalidateDelayed方法 ,通过 ViewRootImpl类的 Handler发送了一条 MSG_INVALIDATE消息 ,继续追踪这条消息的处理可以发现:-10精选文库public void handleMessage(Message msg) .switch (msg.what) case MSG_INVALIDATE:(View)msg.obj).invalidate();break;.invalidate系列方法请求重绘View 树(也就是draw 方法),如果 View 大小没有发生变化就不会调用layout过程,并且只绘制那些“需要重绘的”View ,也就是哪个Vi

温馨提示

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

评论

0/150

提交评论