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(),但不会重新绘制任何视图包括该调用者本身。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容