当前位置:天才代写 > tutorial > 安卓教程 > 深入了解View的绘制流程

深入了解View的绘制流程

2021-02-28 17:43 星期日 所属: 安卓教程 浏览:758

1.  ViewRoot

    ViewRoot是联接WindowManager与DecorView的桥梁,View的全部绘图步骤的三大步走(measure、layout、draw)全是根据ViewRoot进行的。当Activity目标被建立结束后,会将DecorView加上到Window中(Window是对对话框的抽象性,DecorView是一个对话框的顶尖器皿View,其实质是一个FrameLayout),另外会建立ViewRootImpl(ViewRoot的完成类)目标,并将ViewRootImpl与DecorView创建关系。有关ViewRoot,大家只必须了解它是联络GUI智能管理系统和GUI展现系统软件的桥梁。View的绘图步骤从ViewRoot的performTraversals方式逐渐,历经measure、layout、draw三大全过程进行对一个View的绘图工作中。peformTraversal方式內部会启用measure、layout、draw这三个方式,这三个方式內部又各自启用onMeasure、onLayout、onDraw方式。

    在onMeasure方式中View会对其全部的子原素实行measure全过程,这时measure全过程就从父器皿”传送”到子原素中,然后子原素会递归的对他的儿子原素开展measure全过程,这般不断进行对全部View树的遍历。onLayout与onDraw全过程的实行步骤与此相近。

    measure全过程决策了View的精确测量高宽,这一全过程完毕后,就可以根据getMeasuredHeight和getMeasuredWidth得到View的精确测量宽高了;

    layout全过程决策了View在父器皿中的部位和View的最后表明高宽,getTop等方式可获得View的top等四个部位主要参数(View的左上方端点的座标为(left, top), 右下方顶点坐标为(right, bottom)),getWidth和getHeight可得到View的最后表明高宽(width = right – left;height = bottom – top)。 

    draw全过程决策了View最后表明出去的模样,此全过程进行后,View才会在显示屏上表明出去。

   

2. MeasureSpec

    MeasureSpec为一个32位系统的int值,高2位意味着SpecMode,低30位意味着SpecSize,前面一种指精确测量方式,后面一种指某类精确测量方式下的规格型号尺寸。在一个View的measure全过程中,系统软件会将该View的LayoutParams融合父器皿的“规定”转化成一个MeasureSpec,这一MeasureSpec表明了应当如何精确测量这一View。

(1)三种 SpecMode:

    UNSPECIFIED父器皿不对View作一切规定,一般 用以系统软件內部,表明一种精确测量的情况。

    EXACTLY父器皿早已检验出View所必须的精准尺寸,这类精确测量方式下View的精确测量值便是SpecSize的值。这一SpecMode相匹配于LayoutParams中的match_parent和得出实际尺寸这二种方式。

    AT_MOST父器皿特定了一个能用尺寸即SpecSize,View的尺寸不可以超过此值,能用尺寸在于不一样View的实际完成。这一SpecMode相匹配于LayoutParams中的wrap_content。

(2)针对DecorView,它的MeasureSpec由对话框规格和其本身的LayoutParams一同明确;针对一般View,他的MeasureSpec由父器皿的MeasureSpec和其本身的LayoutParams一同明确。

 

3. View的实际绘图步骤

(1)measure全过程

a. DecorView的measure全过程

    前边大家提及过,DecorView是一个运用对话框的根器皿,它实质上是一个FrameLayout。DecorView有唯一一个子View,它是一个竖直LinearLayout,这一竖直线形布局管理器包括两个子原素,一个是TitleView(ActionBar的器皿),另一个是ContentView(对话框內容的器皿)。有关ContentView,它是一个FrameLayout(android.R.id.content),大家平时用的setContentView便是设定它的子View。如下图中,大家为TilteView中加上了一个ActionBar,为ContentView中加上了一个RelativeLayout(根据setContentView方式)。

    前边提及,DecorView的MeasureSpec由对话框的规格和本身的LayoutParams一同决策。在ViewRootImpl的measureHierarchy方式中,完成了建立DecorView的MeasureSpec的全过程,相对的编码精彩片段以下:

1 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    之上编码精彩片段中的childXxxMeasureSpec即是DecorView的MeasureSpec,lp.width和lp.height被系统软件取值为MATCH_PARENT。getRootMeasureSpec的编码以下:

 private int getRootMeasureSpec(int windowSize, int rootDimension) {  
    int measureSpec;  
    switch (rootDimension) {  
        case ViewGroup.LayoutParams.MATCH_PARENT:  
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);  
            break;  
        case ViewGroup.LayoutParams.WRAP_CONTENT:  
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);  
            break;  
        default:  
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);  
            break;  
    }  
    return measureSpec;  
} 

    所述编码中启用了makeMeasureSpec方式来获得measureSpec,而传到的rootDimension主要参数即是lp.width或lp.height,数值MATCH_PARENT,从而必得DecorView的MeasureSpec,在其中SpecMode为EXACTLY,SpecSize为windowSize。

 

 

b. 一般View(非ViewGroup)的measure全过程:

    非ViewGroup的View的特性是不可以有子原素,因而只需精确测量好本身就可以了。一般View的measure根据measure方式来进行:

1 public final void measure(int widthMeasureSpec, int heightMeasureSpec) {  
    //....  
    //回调函数onMeasure()方式    
    onMeasure(widthMeasureSpec, heightMeasureSpec);  
    //more  
}

    一般View的measure方式是由ViewGroup在measureChild方式中启用的(即完成了measure全过程从ViewGroup到子View的传送),ViewGroup启用他的儿子View的measure时即传到了该子View的widthMeasureSpec和heightMeasureSpec。注意到measure是一个final方式,因而要完成自定的measure全过程,必须重写onMeasure方式:

1 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

    setMeasureDimension方式用以设定View的精确测量高宽,如果不重写此方式,默认设置是立即启用getDefaultSize获得规格的:

 public static int getDefaultSize(int size, int measureSpec) {  
    int result = size;  
    int specMode = MeasureSpec.getMode(measureSpec);  
    int specSize = MeasureSpec.getSize(measureSpec);  
    switch (specMode) {  
        case MeasureSpec.UNSPECIFIED:  
            result = size;  
            break;  
        case MeasureSpec.AT_MOST:  
        case MeasureSpec.EXACTLY:  
            result = specSize;  
            break;  
    }  
    return result;  
}  

    由之上编码得知,一切正常状况下(SpecMode为AT_MOST或EXACTLY),getDefaultSize获得的规格尺寸即是specSize。由之上编码还可了解,立即承继View的自定控制必须重写onMeasure方式并设定wrap_content时的本身尺寸,不然在合理布局中应用wrap_content就等同于应用match_parent的实际效果。实例以下:

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    if (widthSpecMode == MeasureSpec.AT_MOST  && heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(mWidth, mHeight);
    } else if (widthSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(mWidth, heightSpecSize);
    } else if (heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(widthSpecSize, mHeight);
    }
}

    所述实例编码中的mWidth,mHeight是为wrap_content时设置的默认设置高宽。这一默认设置高宽可依据具体必须自主设定,例如TextView在wrap_content时的默认设置宽高是依据在其中的全部文本的总宽来设置的,进而完成恰好“包囊”文本內容的实际效果。

 

c. ViewGroup的measure全过程:

    ViewGroup必须先进行子View的measure全过程,才可以进行本身的measure全过程,ViewGroup的onMeasure方式依据不一样的布局管理器类(LinearLayout、RelativeLayout这些)有不一样的完成,例如LinearLayout的onMeasure方式编码以下:

1 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOriention == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

    measureVertical中精确测量子原素的主要编码以下:

 //See how tall everyone is. Also remember max width.
for (int i = 0; i < count;   i) {
    final View child = getVirtualChildAt(i);
    . . .
    //Determine how big this child would like to be. If this or previous children have given a weight, 

//then we allow it to use all available space (and we will shrink things later if needed). measureChildBeforeLayout(child, i, widthMeasureSpec, 0, heightMeasureSpec, totalHeight == 0 ? mTotalLength : 0); if (oldHeight != Integer.MIN_VALUE) { lp.height = oldHeight; } final int childLength = child.getMeasuredHeight(); final int totalLength = mTotalLength; mTotalLength = Math.max(totalLength, totalLength childHeight lp.topMargin lp.bottomMargin getNextLocationOffset(child)); }

    由所述编码能够了解,在measureVertical方式时会对每一个LinearLayout中的子原素开展解析xml并根据measureChildBeforeLayout方式对每一个子原素实行measure全过程。在measureChildBeforeLayout方式內部会启用子原素的measure方式,那样会先后让每一个子原素进到measure全过程。mTotalLength表明LinearLayout在垂直方位上的规格,每进行一个子原素的measure全过程,它的值也会相对提升。精确测量完子原素后,LinearLayout会精确测量本身的尺寸。measureVertical中精确测量LinearLayout本身的关键编码以下:

 //Add in our padding.
mTotalLength  = mPaddingTop   mPaddingBottom;
int heightSize = mTotalLength;
//Check against our minimum height
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
//Reconcile our calculated size with the heightMeasureSpec
int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
. . .
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), heightSizeAndState);

    对竖直的LinearLayout而言,它在水平方向的精确测量全过程与一般View的measure全过程一样,在垂直方位的measure全过程以下:若该竖直LinearLayout的layout_height为match_parent或实际标值,它的measure全过程与一般View一样;若该竖直LinearLayout的layout_height为wrap_content,则它垂直方位的高宽比为全部子原素占有的高宽比之和,但不可以超出父器皿的能用室内空间尺寸,最后高宽比也要充分考虑其垂直方位的padding,有关的编码以下:

 public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);
    switch (speczMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
            if (specSize < size) {
                result = specSize | MEASURED_STATE_TOO_SMALL;
            } else {
                result = size;
            }
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result | (childMeasuredState & MEASURED_STATE_MASK);
}

   

    ViewGroup关键根据其measureChildren方式进行他的儿子View的measure全过程,上边竖直LinearLayout中启用的measureChildBeforeLayout能够看作是measureChildren的一个“变异”,measureChildren方式编码以下:

 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {  
    final int size = mChildrenCount;  
    final View[] children = mChildren;  
    for (int i = 0; i < size;   i) {  
            final View child = children[i];  
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 
                measureChild(child, widthMeasureSpec, heightMeasureSpec);  
            }  
    }  
}  

    在其中,measureChild方式进行对联View的measure全过程:

1 protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {  
    final LayoutParams lp = child.getLayoutParams();  
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
        mPaddingLeft   mPaddingRight, lp.width);  
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,  
        mPaddingTop   mPaddingBottom, lp.height);  
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
}  

    留意在这儿,在实行child.measure方式前,就早已根据getChildMeasureSpec获得了子View的MeasureSpec。getChildMeasureSpec依据子View的LayoutParams和父器皿的MeasureSpec来决策子View的MeasureSpec,getChildMeasureSpec的编码以下:

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    //这儿传到的spec为ViewGroup的MeasureSpec
    //specMode和specSize即为父器皿的MeasureSpec
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    //padding为父器皿中已应用的室内空间尺寸,size为父器皿能用室内空间尺寸
    int size = Math.max(0, specSize - padding);
    int resultSize = 0;
    int resultMode = 0;

    switch (specMode) {
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                //子View要想和父器皿一样大
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                //子View想自身决策它的尺寸,但不可以比父器皿大
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } 
            break;
        //Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
    

    之上涵数的工作中全过程可小结以下:

    a. 当childLayoutParams特定为实际的大钟头:若parentSpecMode为EXACTLY,则childSpecMode为EXACTLY,childSpecSize为childSize(layout_width和layout_height中特定的实际尺寸);若parentSpecMode为AT_MOST,则childSpecMode和childSpecSize各自为EXACTLY和childSize。

    b. 当childLayoutParams为match_parent时:若parentSpecMode为EXACTLY,则childSpecMode和childSpecSize各自为EXACTLY和parentSize(父器皿中能用的尺寸);若parentSpecMode为AT_MOST,则childSpecMode和childSpecSize各自为AT_MOST和parentSize。

    c. 当childLayoutParams为wrap_content时:若parentSpecMode为EXACTLY,则childSpecMode和childSpecSize各自为AT_MOST和parentSize;若parentSpecMode为AT_MOST,则childSpecMode和childSpecSize各自为AT_MOST和parentSize。

     也有一点必须留意的是,View的measure全过程和Activity生命期的回调函数方式并不是同歩的,也就是不可以确保在某一生命期的回调函数方式中measure全过程早已实行结束。

 

(2)layout全过程

    layout全过程用于明确View在父器皿中的部位,因而是由父器皿获得子View的部位主要参数后,启用child.layout方式并传到已获得的部位主要参数,进而进行对联View的layout。当ViewGroup的部位被明确后,它在onLayout中会解析xml全部子原素并启用其layout方式,在layout方式中子原素的onLayout又会被启用。layout方式明确先View自身的部位,再启用onLayout方式明确全部子原素的部位。layout方式以下:

1 public void layout(int l, int t, int r, int b) {  
 2     ……
    int oldL = mLeft;  
    int oldT = mTop;  
    int oldB = mBottom;  
    int oldR = mRight;  
    boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : set(l, t, r, b);
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {  
 9         onLayout(changed, l, t, r, b);  
        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; 
        ListenerInfo li = mListenerInfo; 
        if (li != null && li.mOnLayoutChangeListeners != null) {  
            ArrayList<OnLayoutChangeListener> listenersCopy =  
                (ArrayList<OnLayoutChangeListener>) li.mOnLayoutChangeListeners.clone();  
            int numListeners = listenersCopy.size();  
            for (int i = 0; i < numListeners;   i) {  
                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);  
18             }  
19         }  
20     }          
    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; 
    mPrivateFlags3 |= PFLAGS3_IS_LAID_OUT; 
} 

    layout方式的大概步骤:最先根据setFrame方式设置View的四个部位主要参数,即用传出的l、t、r、b四个主要参数复位mLeft、mTop、mRight、mBottom这四个值,进而明确了该View在父器皿中的部位。若部位发生改变就启用onLayout方式,onLayout方式在View类中为空,由于对联原素合理布局的工作中仅有器皿View才必须做。在ViewGroup中,onLayout是一个抽象方法,由于针对不一样的布局管理器类,对联原素的合理布局方法是不一样的。例如,LinearLayout的onLayout方式以下:

1 protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (mOriention == VERTIVAL) {
        layoutVertical(l, t, r, b);
    } else {
        layoutHorizontal(l, t, r, b);
    }
}

    之上编码会依据LinearLayout的orientation为水准或竖直启用相对的涵数来进行合理布局全过程,这儿以layoutVertical为例子剖析一下竖直线形布局管理器的合理布局全过程,layoutVertical的关键编码以下:

 void layoutVertical(int left, int top, int right, int bottom) {
    . . .
    final int count = getVirtualChildCount();
    for (int i = 0; i < count; i  ) {
        final View child = getVirtualChildAt(i);
        if (child == null) {
            childTop  = measureNullChild(i);
        } else if (child.getVisibility() != GONE) {
            final int childWidth = child.getMeasuredWidth();
            final int childHeight = child.getMeasuredHeight();
            final int LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
            . . .
            if (hasDividerBeforeChildAt(i)) {
                childTop  = mDividerHeight;
            }
            childTop  = lp.topMargin;
            setChildFrame(child, childLeft, childTop   getLocationOffset(child), childWidth, childHeight);
            childTop  = childHeight   lp.bottomMargin   getNextLocationOffset(child);
            i  = getChildrenSkipCount(child, i);
        }
    }
}

      之上编码中,LinearLayout会解析xml它的全部子View,并启用setChildFrame方式设定子View的部位,编码中的childTop意味着当今子View的top部位主要参数。setChildFrame方式的编码以下:    

1 private void setChildFrame(View child, int left, int top, int width, int  height) {
    child.layout(left, top, left   width, top   height);
}

      换句话说,在父器皿(这儿为LinearLayout)完成了对联原素部位主要参数(top、left、right、bottom)的获得后,会启用子原素的layout方式,并把获得到的子原素部位主要参数传到,进而进行对联原素的layout全过程。子原素在自身的layout方式中,也会先进行对自身的合理布局(明确四个部位主要参数),再启用onLayout方式进行对他的儿子View的合理布局,那样layout全过程就顺着View树一层层传了下来。

      layout全过程进行后,便能够根据getWidth和getHeight方式获得View的最后表明高宽,这俩方式源代码以下:

1 public final int getWidth() {
    return mRight – mLeft;
}    
1 public final int getHeight() {
    return mBottom – mTop;
}

     从而便能够了解,根据getMeasuredWidth/getMeasuredHeight方式获得的精确测量高宽与根据getWidth/getHeight方式获得的最后表明高宽的差别:即最后表明宽高是根据View的部位主要参数做差获得的,一切正常状况下应当与精确测量高宽相同。但如果我们重写View的layout方式以下:

1 public void layout(int l, int t, int r, int b) {
    super.layout(l, t, r, b, r   100, b   100);
}

    那样便会造成最后表明高宽比精确测量宽又高又大100。(除非是你很确立的了解自身要想做什么,不然不应该那样做)

 

(3)draw全过程

    关键分成下列六步:
    a. 绘图情况;
    b. 假如要主视图表明渐变色框,这儿会做一些准备工作;
    c.
绘图主视图自身,即启用onDraw()涵数。在View中onDraw()是个空涵数,换句话说实际的主视图都需要override该涵数来完成自身的表明,而针对ViewGroup则不用完成该涵数,由于做为器皿是“沒有內容“的,其包括了好几个子view,而子view早已完成了自身的绘图方式,因而只必须告知子view绘图自身就可以了,也就是下边的dispatchDraw()方式;
 
  d.
绘图子主视图,即dispatchDraw()涵数。在View中它是个空涵数,实际的主视图不用完成该方式,它是专业为容器类提前准备的,也就是容器类务必完成该方式;
 
  e. 假如必须, 逐渐绘图渐变色框;
    f. 绘图下拉列表;

    draw方式的编码以下:   

 public void draw(Canvas canvas) {  
    final int privateFlags = mPrivateFlags;  
    final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&  
        (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);  
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;  
    // Step 1, draw the background, if needed  
    int saveCount;  
    if (!dirtyOpaque) {  
        drawBackground(canvas);
    }  
    //step 2 & 5 
    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);  
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
            // we're done...
            return;  
        }
    }  
}  

   View的draw全过程的传送根据diapatchDraw来完成,dispatchDraw会解析xml启用全部子View的draw方式,那样draw事情就一层层传了下来。重写View的onDraw方式能够订制View绘图出去的模样,比如完成一些独特的图型和动漫。

   View有一个名叫setWillNotDraw的方式,若一个View不用绘图一切內容,可根据这一方式将相对标识设成true,系统软件会开展相对提升。ViewGroup默认设置打开这一标识,View默认设置不打开。

 

之上就是我学习培训View的绘图步骤后的简易小结,许多地区描述的还不够清楚精确,如有什么问题热烈欢迎大伙儿在发表评论一起探讨 🙂

 

    关键字:

天才代写-代写联系方式