到目前为止,我们已经学习了View的测量,布局过程,今天我们就来学习一下最后一个过程:绘画
绘画过程和前面的两个过程一样,都是在ViewRoot的performTraversals这个方法中调用的,感兴趣的同学可以找找看,我这里就不在贴出代码了,我们直接看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 {@link #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步:
onDraw()
方法,View的设计者可以在onDraw()
方法中根据自己的需要,绘制内容dispatchDraw()
方法绘制子视图,如果视图内没有子视图,则不需要绘制,也就是说自由ViewGroup
的子类需要重载dispatchDraw
方法onDrawScrollbars()
方法绘制滚动条,当然前提是需要显示滚动条 现在View的绘制机制就算讲解完了,接下来我们讲解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>
<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方法。
在这里不知道大家发现了一个问题没,我的布局文件如下:
<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其实就是调用inflate
方法,我们看看此方法的原型吧
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
*
* @param resource ID for an XML layout resource to load (e.g.,
* R.layout.main_page
)
* @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
* {@link InflateException} if there is an error.
*
* @param resource ID for an XML layout resource to load (e.g.,
* R.layout.main_page
)
* @param root Optional view to be the parent of the generated hierarchy (if
* attachToRoot is true), or else simply an object that
* provides a set of LayoutParams values for root of the returned
* hierarchy (if attachToRoot 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.
*
* Important 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
* attachToRoot is true), or else simply an object that
* provides a set of LayoutParams values for root of the returned
* hierarchy (if attachToRoot 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(" 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;
}
}
这个方法看起来比较长,但是逻辑非常简单,主要有如下步骤:
createViewFromTag
方法创建ViewgenerateLayoutParams
方法,如果第三个参数为false,并将结果设置到上面创建的View中rInflate
对View的子View进行解析,此时View就充当了root的角色,也就是说在后面的遍历中第二个参数不为null,第三个参数为trueroot.addView(temp, params)
其中params就是第二部创建的。最后就是如果root!=null && attach==true,那么返回root,否则返回第一步创建的View。
好了,到这里所有有关View的绘制的机制都已经学习完了,有什么没有写明白的地方欢迎大家留言讨论》。。