Android Bitmap 知识点梳理

View绘制基本流程

Android系统View的绘制流程:依次执行View类里面的如下三个方法:
measure(int ,int) :测量View的大小

layout(int ,int ,int ,int) :设置子View的位置

draw(Canvas) :绘制View内容到Canvas画布上

整个View树的绘图流程是在ViewRoot.Java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为根据之前设置的状态,判断是否需要重新计算视图大小(measure)、是否重新需要安置视图的位置(layout)、以及是否需要重绘 (draw)

mesarue()测量过程

主要作用:为整个View树计算实际的大小,即设置实际的高(mMeasuredHeight)和宽(mMeasureWidth),每个View的控件的实际宽高都是由父视图和本身视图决定的。

具体的调用如下:

ViewRootImpl 的performTraversals方法中,调用measureHierarchy,然后调用performMeasure

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {  
       Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");  
       try {  
           mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
       } finally {  
           Trace.traceEnd(Trace.TRACE_TAG_VIEW);  
       }  
    }

ViewRoot根对象地属性mView(其类型一般为ViewGroup类型)调用measure()方法去计算View树的大小,回调View/ViewGroup对象的onMeasure()方法,该方法实现的功能如下:

1、设置本View视图的最终大小,该功能的实现通过调用setMeasuredDimension()方法去设置实际的高(mMeasuredHeight)和宽(mMeasureWidth)

2、如果该View对象是个ViewGroup类型,需要重写onMeasure()方法,对其子视图进行遍历的measure()过程。

对每个子视图的measure()过程,是通过调用父类ViewGroup.java类里的measureChildWithMargins()方法去实现,该方法内部只是简单地调用了View对象的measure()方法。

整个measure调用流程就是个树形的递归过程measure()方法两个参数都是父View传递过来的,也就是代表了父view的规格。他由两部分组成,高2位表示MODE,定义在MeasureSpec类(View的内部类)中,有三种类型,MeasureSpec.EXACTLY表示确定大小, MeasureSpec.AT_MOST表示最大大小, MeasureSpec.UNSPECIFIED不确定。低30位表示size,也就是父View的大小。对于系统Window类的DecorVIew对象Mode一般都为MeasureSpec.EXACTLY ,而size分别对应屏幕宽高。对于子View来说大小是由父View和子View共同决定的。

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

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {  
 boolean optical = isLayoutModeOptical(this);  
 if (optical != isLayoutModeOptical(mParent)) {  
     Insets insets = getOpticalInsets();  
     int opticalWidth  = insets.left + insets.right;  
     int opticalHeight = insets.top  + insets.bottom;  

     measuredWidth  += optical ? opticalWidth  : -opticalWidth;  
     measuredHeight += optical ? opticalHeight : -opticalHeight;  
 }  
 mMeasuredWidth = measuredWidth;  
 mMeasuredHeight = measuredHeight;  

 mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;  
}

layout布局过程

主要作用 :为将整个根据子视图的大小以及布局参数将View树放到合适的位置上。

具体的调用如下:

ViewRootImpl 的performTraversals方法中,调用performLayout

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,  
    int desiredWindowHeight) {  
mLayoutRequested = false;  
mScrollMayChange = true;  
mInLayout = true;  

final View host = mView;  
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {  
    Log.v(TAG, "Laying out " + host + " to (" +  
            host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");  
}  

Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");  
try {  
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());  

    mInLayout = false;  
    int numViewsRequestingLayout = mLayoutRequesters.size();  
    if (numViewsRequestingLayout > 0) {  
        // requestLayout() was called during layout.  
        // If no layout-request flags are set on the requesting views, there is no problem.  
        // If some requests are still pending, then we need to clear those flags and do  
        // a full request/measure/layout pass to handle this situation.  
        ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,  
                false);  
        if (validLayoutRequesters != null) {  
            // Set this flag to indicate that any further requests are happening during  
            // the second pass, which may result in posting those requests to the next  
            // frame instead  
            mHandlingLayoutInLayoutRequest = true;  

            // Process fresh layout requests, then measure and layout  
            int numValidRequests = validLayoutRequesters.size();  
            for (int i = 0; i < numValidRequests; ++i) {  
                final View view = validLayoutRequesters.get(i);  
                Log.w("View", "requestLayout() improperly called by " + view +  
                        " during layout: running second layout pass");  
                view.requestLayout();  
            }  
            measureHierarchy(host, lp, mView.getContext().getResources(),  
                    desiredWindowWidth, desiredWindowHeight);  
            mInLayout = true;  
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());  

            mHandlingLayoutInLayoutRequest = false;  

            // Check the valid requests again, this time without checking/clearing the  
            // layout flags, since requests happening during the second pass get noop'd  
            validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);  
            if (validLayoutRequesters != null) {  
                final ArrayList<View> finalRequesters = validLayoutRequesters;  
                // Post second-pass requests to the next frame  
                getRunQueue().post(new Runnable() {  
                    @Override  
                    public void run() {  
                        int numValidRequests = finalRequesters.size();  
                        for (int i = 0; i < numValidRequests; ++i) {  
                            final View view = finalRequesters.get(i);  
                            Log.w("View", "requestLayout() improperly called by " + view +  
                                    " during second layout pass: posting in next frame");  
                            view.requestLayout();  
                        }  
                    }  
                });  
            }  
        }  

    }  
} finally {  
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);  
}  
mInLayout = false;  

}

draw()绘图过程

ViewRootImpl 的performTraversals方法中,调用了mView的draw方法

mView.draw()开始绘制,draw()方法实现的功能如下:

1 、绘制该View的背景

2 、为显示渐变框做一些准备操作

3、调用onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法)

4、调用dispatchDraw ()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)

值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法。

5、绘制滚动条

刷新视图

Android中实现view的更新有两个方法,一个是invalidate,另一个是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。

requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。

说明:只是对View树重新布局layout过程包括measure()和layout()过程,不会调用draw()过程,但不会重新绘制
任何视图包括该调用者本身。
一般引起invalidate()操作的函数如下:

1、直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。

2、setSelection()方法 :请求重新draw(),但只会绘制调用者本身。

3、setVisibility()方法 : 当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。

4 、setEnabled()方法 : 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。

发布了43 篇原创文章 · 获赞 3 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章