当前位置: 代码迷 >> Android >> Android 中View的作图机制源码分析 四
  详细解决方案

Android 中View的作图机制源码分析 四

热度:679   发布时间:2016-04-27 23:48:32.0
Android 中View的绘制机制源码分析 四

到目前为止,我们已经学习了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步:

  1. 绘制背景,这里使用了dirtyOpaque表示dirty区是否是不透明的,如果是透明的,那么绘制背景,在Android系统中,基本上dirtyOpaque基本上都是false的,所以背景区基本上都会绘制
  2. 绘制视图的渐变框,但是大多数情况下都不需要绘制渐变框,因此这里直接跳过
  3. 绘制视图本身,绘制视图本身调用的是onDraw() 方法,View的设计者可以在onDraw() 方法中根据自己的需要,绘制内容
  4. 调用dispatchDraw() 方法绘制子视图,如果视图内没有子视图,则不需要绘制,也就是说自由ViewGroup 的子类需要重载dispatchDraw 方法
  5. 调用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>&nbsp;&nbsp;&nbsp;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;        }    }

这个方法看起来比较长,但是逻辑非常简单,主要有如下步骤:

  1. 创建View ,通过调用createViewFromTag 方法创建View
  2. 如果root不等于null,则调用root的generateLayoutParams 方法,如果第三个参数为false,并将结果设置到上面创建的View中
  3. 调用rInflate 对View的子View进行解析,此时View就充当了root的角色,也就是说在后面的遍历中第二个参数不为null,第三个参数为true
  4. 最后如果root!=null && attach ==true,那么调用root的addView方法root.addView(temp, params) 其中params就是第二部创建的。

最后就是如果root!=null && attach==true,那么返回root,否则返回第一步创建的View。

好了,到这里所有有关View的绘制的机制都已经学习完了,有什么没有写明白的地方欢迎大家留言讨论》。。

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案