到目前为止,我们已经学习了View的测量,布局过程,今天我们就来学习一下最后一个过程:绘画
绘画过程和前面的两个过程一样,都是在ViewRoot的performTraversals这个方法中调用的,感兴趣的同学可以找找看,我这里就不在贴出代码了,我们直接看View的draw方法吧
View的draw过程的五部曲
/** * Manually render this view (and all of its children) to the given Canvas. * The view must have already done a full layout before this function is * called. When implementing a view, do not override this method; instead, * you should implement [email protected] #onDraw}. * * @param canvas The Canvas to which the View is rendered. */ public void draw(Canvas canvas) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } final int privateFlags = mPrivateFlags; final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE && (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState); mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN; /* * 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 int saveCount; if (!dirtyOpaque) { final Drawable background = mBGDrawable; if (background != null) { final int scrollX = mScrollX; final int scrollY = mScrollY; if (mBackgroundSizeChanged) { background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); mBackgroundSizeChanged = false; } if ((scrollX | scrollY) == 0) { background.draw(canvas); } else { canvas.translate(scrollX, scrollY); background.draw(canvas); canvas.translate(-scrollX, -scrollY); } } } // skip step 2 & 5 if possible (common case) final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); // we're done... return; } /* * Here we do the full fledged routine... * (this is an uncommon case where speed matters less, * this is why we repeat some of the tests that have been * done above) */ boolean drawTop = false; boolean drawBottom = false; boolean drawLeft = false; boolean drawRight = false; float topFadeStrength = 0.0f; float bottomFadeStrength = 0.0f; float leftFadeStrength = 0.0f; float rightFadeStrength = 0.0f; // Step 2, save the canvas' layers int paddingLeft = mPaddingLeft; int paddingTop = mPaddingTop; final boolean offsetRequired = isPaddingOffsetRequired(); if (offsetRequired) { paddingLeft += getLeftPaddingOffset(); paddingTop += getTopPaddingOffset(); } int left = mScrollX + paddingLeft; int right = left + mRight - mLeft - mPaddingRight - paddingLeft; int top = mScrollY + paddingTop; int bottom = top + mBottom - mTop - mPaddingBottom - paddingTop; if (offsetRequired) { right += getRightPaddingOffset(); bottom += getBottomPaddingOffset(); } final ScrollabilityCache scrollabilityCache = mScrollCache; int length = scrollabilityCache.fadingEdgeLength; // clip the fade length if top and bottom fades overlap // overlapping fades produce odd-looking artifacts if (verticalEdges && (top + length > bottom - length)) { length = (bottom - top) / 2; } // also clip horizontal fades if necessary if (horizontalEdges && (left + length > right - length)) { length = (right - left) / 2; } if (verticalEdges) { topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength())); drawTop = topFadeStrength >= 0.0f; bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength())); drawBottom = bottomFadeStrength >= 0.0f; } if (horizontalEdges) { leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength())); drawLeft = leftFadeStrength >= 0.0f; rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength())); drawRight = rightFadeStrength >= 0.0f; } saveCount = canvas.getSaveCount(); int solidColor = getSolidColor(); if (solidColor == 0) { final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; if (drawTop) { canvas.saveLayer(left, top, right, top + length, null, flags); } if (drawBottom) { canvas.saveLayer(left, bottom - length, right, bottom, null, flags); } if (drawLeft) { canvas.saveLayer(left, top, left + length, bottom, null, flags); } if (drawRight) { canvas.saveLayer(right - length, top, right, bottom, null, flags); } } else { scrollabilityCache.setFadeColor(solidColor); } // 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 final Paint p = scrollabilityCache.paint; final Matrix matrix = scrollabilityCache.matrix; final Shader fade = scrollabilityCache.shader; final float fadeHeight = scrollabilityCache.fadingEdgeLength; if (drawTop) { matrix.setScale(1, fadeHeight * topFadeStrength); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); canvas.drawRect(left, top, right, top + length, p); } if (drawBottom) { matrix.setScale(1, fadeHeight * bottomFadeStrength); matrix.postRotate(180); matrix.postTranslate(left, bottom); fade.setLocalMatrix(matrix); canvas.drawRect(left, bottom - length, right, bottom, p); } if (drawLeft) { matrix.setScale(1, fadeHeight * leftFadeStrength); matrix.postRotate(-90); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); canvas.drawRect(left, top, left + length, bottom, p); } if (drawRight) { matrix.setScale(1, fadeHeight * rightFadeStrength); matrix.postRotate(90); matrix.postTranslate(right, top); fade.setLocalMatrix(matrix); canvas.drawRect(right - length, top, right, bottom, p); } canvas.restoreToCount(saveCount); // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); }
该方法比较长,但是内部的执行流程正如源码中注释的,主要分为5步:
- 绘制背景,这里使用了dirtyOpaque表示dirty区是否是不透明的,如果是透明的,那么绘制背景,在Android系统中,基本上dirtyOpaque基本上都是false的,所以背景区基本上都会绘制
- 绘制视图的渐变框,但是大多数情况下都不需要绘制渐变框,因此这里直接跳过
- 绘制视图本身,绘制视图本身调用的是
onDraw()
方法,View的设计者可以在onDraw()
方法中根据自己的需要,绘制内容 - 调用
dispatchDraw()
方法绘制子视图,如果视图内没有子视图,则不需要绘制,也就是说自由ViewGroup
的子类需要重载dispatchDraw
方法 - 调用
onDrawScrollbars()
方法绘制滚动条,当然前提是需要显示滚动条
经过上面的5个步骤,View的绘制过程差不多就结束了。这里我们重点要关注第3步和第4步,因为我们在自定义View或者ViewGroup的时候基本上就是改写这两步中涉及到的方法
现在View的绘制机制就算讲解完了,接下来我们讲解LayoutParams这个类的相关知识吧
LayoutParams你必须知道的秘密
LayoutParams这个类相信对于每个Android开发人员是再熟悉不过了,但是并不是每个人对这个类都有深入的理解。今天我们就来学习一下LayoutParams你必须知道的秘密。
先来看看官方对于这个类的解释:
LayoutParams are used by views to tell their parents how they want to be laid out. See ViewGroup Layout Attributes for a list of all child view attributes that this class supports.
这个类用于子视图告诉他们的父视图,他们想要怎么显示出来,也就是告诉父类自己的一些属性。
先从一个简单的例子开始:向LinearLayotu中添加一个View
布局文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/line_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" ></LinearLayout>
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tv_child" android:layout_height="match_parent" android:layout_width="match_parent" android:background="#ffccee" android:text="hello my name is yuanzeyao" ></TextView>
代码如下:
LinearLayout parent= (LinearLayout) this.findViewById(R.id.line_parent); View view= LayoutInflater.from(this).inflate(R.layout.layout,null); parent.addView(view);
运行效果如下:
代码很简单,就是先使用LayoutInflater生成一个View,然后添加到LinearLayout中去,注意这里我们并没有传递LayoutParams相关类型进去的,那View是如何告诉LinearLayout它的LayoutParams呢。我们进入到addView方法看看,你会发现最终调用的是ViewGroup中的如下方法
public void addView(View child, int index) { LayoutParams params = child.getLayoutParams(); if (params == null) { params = generateDefaultLayoutParams(); if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params); }
代码比较简单,先通过getLayoutParams方法拿到LayoutParams对象,如果此对象不为空,那么直接调用addView(child,index,params)
如果为空,那么久调用generateDefaultLayoutParams方法生成一个LayoutParams对象,我们进入源码看看ViewGroup中的generaterDefaultLayoutParams
方法是如何生存的一个LayoutParams的吧
protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); }
这里就是简单的创建了一个ViewGroup.Layoutparams对象,但是要知道我们这里是调用的LinearLayout的generateDefaultLayoutParams方法,所以我们看看LinearLayout是如何改写此方法的。
@Override protected LayoutParams generateDefaultLayoutParams() { if (mOrientation == HORIZONTAL) { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } else if (mOrientation == VERTICAL) { return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } return null; }
LinearLayout里面的这个方法也很简单,唯一不同的就是它创建的是LinearLayout.LayoutParams类的这个对象,那么LinearLayout.LayoutParams和ViewGroup.LayoutParams有什么关系呢?同样,看看源码就知道了。通过查看源码你会发现,LinearLayout.LayoutParams这个类是继承自ViewGroup.MarginLayoutParams这个类,而这个类又继承了ViewGroup.LayoutParams这个类,其实LinearLayout,RelativeLayout,FrameLayout等布局里面的LayoutParams都是继承自ViewGroup.MarginLayoutParams,继承关系如下:
其实通过名称,我们就大概可以猜出ViewGroup.LayoutParams和ViewGroup.MarginLayoutParmas的区别了,那就是MarginLayoutParams支持margin属性了,也就是我们在xml文件中配置的leftMargin、topMargin等属性。我们看看源码是不是真的是这样
public MarginLayoutParams(Context c, AttributeSet attrs) { super(); TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); setBaseAttributes(a, R.styleable.ViewGroup_MarginLayout_layout_width, R.styleable.ViewGroup_MarginLayout_layout_height); int margin = a.getDimensionPixelSize( com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); if (margin >= 0) { leftMargin = margin; topMargin = margin; rightMargin= margin; bottomMargin = margin; } else { leftMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0); topMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0); rightMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0); bottomMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0); } a.recycle(); }
果不其然,就是在解析leftMargin,topMargin等属性。看完了MarginLayoutParams的功能后,我们看看LinearLayout.LayoutParams这个类在父类的基础上增加了上面功能
public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout); weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0); gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1); a.recycle(); }
主要添加了两个属性:weight属性和gravity属性,我们再看看RelatvieLayout.LayoutParams里面增加了上面属性吧
public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.RelativeLayout_Layout); final int[] rules = mRules; final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); switch (attr) { case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing: alignWithParent = a.getBoolean(attr, false); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf: rules[LEFT_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf: rules[RIGHT_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above: rules[ABOVE] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below: rules[BELOW] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline: rules[ALIGN_BASELINE] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft: rules[ALIGN_LEFT] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop: rules[ALIGN_TOP] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight: rules[ALIGN_RIGHT] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom: rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft: rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop: rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight: rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom: rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent: rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal: rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical: rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0; break; } } a.recycle(); }
在RelativeLayout.LayoutParams中添加的属性比较多了,主要是关于对齐方式的,不过你要注意喽,在RelativeLayout.LayoutParams中并没有weight属性和gravity属性。看到这里我想你应该知道为什么LineayLayout不支持对齐方式属性,RelativeLayot不支持gravity和weight属性了。
但是在LinearLayout.gener的generateDefaultLayoutParams
方法中调用的是
public LayoutParams(int width, int height) { super(width, height); weight = 0; }
这个方法,其中默认weight=0,gravity=-1。
看到这里,大家是不是还有一个疑问,在上面的addView()
中,调用child.getLayoutParams()
方法返回是否为null,我们打印一下log就知道,代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayout parent= (LinearLayout) this.findViewById(R.id.line_parent); View view= LayoutInflater.from(this).inflate(R.layout.layout,null); Log.d("yzy","LayoutParams is null:"+(view.getLayoutParams()==null)); parent.addView(view); }
结果:
07-25 14:48:11.842 26143-26143/? D/yzy﹕ LayoutParams is null:true
也就是说调用的是LinearLayout的generateDefaultLayoutParams方法。
在这里不知道大家发现了一个问题没,我的布局文件如下:
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tv_child" android:layout_height="match_parent" android:layout_width="match_parent" android:background="#ffccee" android:text="hello my name is yuanzeyao" ></TextView>
但是TextView并不是全屏的,这是因为我们在加入TextView的时候并没有传入LayoutParams这个参数,而是通过generateDefaultLayoutParams创建的一个默认的LayoutParams
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
,那么在什么时候addView
的时候getLayoutParams
这个返回值不等于null,这就得看看这个View是怎么来的了,下面我们就来分析View的创建过程。
LayoutInflater创建View过程分析
通过LayoutInflater创建View其实就是调用inflate
方法,我们看看此方法的原型吧
/** * Inflate a new view hierarchy from the specified xml resource. Throws * [email protected] InflateException} if there is an error. * * @param resource ID for an XML layout resource to load (e.g., * <code>R.layout.main_page</code>) * @param root Optional view to be the parent of the generated hierarchy. * @return The root View of the inflated hierarchy. If root was supplied, * this is the root View; otherwise it is the root of the inflated * XML file. */ public View inflate(int resource, ViewGroup root) { return inflate(resource, root, root != null); }
此方法有两个参数
resource :表示布局文件id
root : 创建的View的父视图
它的实现也是非常的简单,调用的是另外一个inflate的方法,代码如下:
/** * Inflate a new view hierarchy from the specified xml resource. Throws * [email protected] InflateException} if there is an error. * * @param resource ID for an XML layout resource to load (e.g., * <code>R.layout.main_page</code>) * @param root Optional view to be the parent of the generated hierarchy (if * <em>attachToRoot</em> is true), or else simply an object that * provides a set of LayoutParams values for root of the returned * hierarchy (if <em>attachToRoot</em> is false.) * @param attachToRoot Whether the inflated hierarchy should be attached to * the root parameter? If false, root is only used to create the * correct subclass of LayoutParams for the root view in the XML. * @return The root View of the inflated hierarchy. If root was supplied and * attachToRoot is true, this is root; otherwise it is the root of * the inflated XML file. */ public View inflate(int resource, ViewGroup root, boolean attachToRoot) { if (DEBUG) System.out.println("INFLATING from resource: " + resource); XmlResourceParser parser = getContext().getResources().getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
这里多了一个bool参数,此参数的意思是,是否将创建的View add到制定的root父视图上,对inflate(int resource,ViewGroup root)
这个方法来说,它其实是调用inflate(int resource,ViewGroup root,bool attach)
这个方法,并且只要root 不等于null,那么创建的View就会add到root上,并返还root,也就是说root不等于null,则第三个参数就是true。看到这里,我们回头看看我们在创建View的时候,我们传入的第二个参数是null,我们不妨设置为LinearLayout试试,代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayout parent= (LinearLayout) this.findViewById(R.id.line_parent); View view= LayoutInflater.from(this).inflate(R.layout.layout,parent,false); Log.d("yzy","LayoutParams is null:"+(view.getLayoutParams()==null)); parent.addView(view); }
这里我们将第三个参数设置为false,因为如果不设置,默认是true的,我们看看运行结果吧:
看到没,这里的TextView是全屏的,那如果我们把第三个参数设置为true,并且去掉addView方法试试,代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayout parent= (LinearLayout) this.findViewById(R.id.line_parent); View view= LayoutInflater.from(this).inflate(R.layout.layout,parent,true); Log.d("yzy","LayoutParams is null:"+(view.getLayoutParams()==null)); //parent.addView(view); }
结果:
我们发现,结果和上面的一样,想知道为什么是这样的吗,那么继续往下看吧,看看inflate里面到底干了上面,经过不断的跟踪,你会发现最终调用到了的是这个方法:
/** * Inflate a new view hierarchy from the specified XML node. Throws * {@link InflateException} if there is an error. * <p> * <em><strong>Important</strong></em> For performance * reasons, view inflation relies heavily on pre-processing of XML files * that is done at build time. Therefore, it is not currently possible to * use LayoutInflater with an XmlPullParser over a plain XML file at runtime. * * @param parser XML dom node containing the description of the view * hierarchy. * @param root Optional view to be the parent of the generated hierarchy (if * <em>attachToRoot</em> is true), or else simply an object that * provides a set of LayoutParams values for root of the returned * hierarchy (if <em>attachToRoot</em> is false.) * @param attachToRoot Whether the inflated hierarchy should be attached to * the root parameter? If false, root is only used to create the * correct subclass of LayoutParams for the root view in the XML. * @return The root View of the inflated hierarchy. If root was supplied and * attachToRoot is true, this is root; otherwise it is the root of * the inflated XML file. */ public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { final AttributeSet attrs = Xml.asAttributeSet(parser); mConstructorArgs[0] = mContext; View result = root; try { // Look for the root node. int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty } if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription() + ": No start tag found!"); } final String name = parser.getName(); if (DEBUG) { System.out.println("**************************"); System.out.println("Creating root view: " + name); System.out.println("**************************"); } if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, attrs); } else { // Temp is the root view that was found in the xml View temp = createViewFromTag(name, attrs); ViewGroup.LayoutParams params = null; if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp rInflate(parser, temp, attrs); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (IOException e) { InflateException ex = new InflateException( parser.getPositionDescription() + ": " + e.getMessage()); ex.initCause(e); throw ex; } return result; } }
这个方法看起来比较长,但是逻辑非常简单,主要有如下步骤:
- 创建View ,通过调用
createViewFromTag
方法创建View - 如果root不等于null,则调用root的
generateLayoutParams
方法,如果第三个参数为false,并将结果设置到上面创建的View中 - 调用
rInflate
对View的子View进行解析,此时View就充当了root的角色,也就是说在后面的遍历中第二个参数不为null,第三个参数为true - 最后如果root!=null && attach ==true,那么调用root的addView方法
root.addView(temp, params)
其中params就是第二部创建的。
最后就是如果root!=null && attach==true,那么返回root,否则返回第一步创建的View。
好了,到这里所有有关View的绘制的机制都已经学习完了,有什么没有写明白的地方欢迎大家留言讨论》。。
版权声明:本文为博主原创文章,未经博主允许不得转载。