自定义View

View的绘制流程一.自定义View分为measure、Layout、draw三大过程ViewRoot对应于ViewRootImpl类,他是链接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并将ViewRootImpl和DecorView产生关联root = new ViewRootImpl(view.getContent(),display);Root.setView(view,wparams,panelParentView);View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout、Draw三个过程Measure:用于测量View的宽高Layout:用于确定View在父容器的摆放位置Draw:用于将View绘制在屏幕上PerformTraversals会依次调用performMeasure、performLayout和performDraw方法,这三个方法分别完成顶级父类的measure、layout、Draw这三大流程。这其中performMeasure方法调用measure方法,在measure方法中调用OnMeasure方法,早OnMeasure方法中对所有的View进行测量。其他的两个过程也是同理可得。唯一不同的是performDraw的传递过程在draw方法中通过dispatchDraw来实现的。1. measure决定了View的宽高,在measure完成后可以通过getMeasureWidth和getMeasureHeight来获取测量的宽高2. Layout:决定了View的四个顶点和实际的宽高,完成后可以通过getTop、getBottomgetLeft和getRight拿到四个顶点的位置,并可以通过getWidth和getHeight拿到View的最终宽高3. draw:决定了View的显示,只有draw方法完成后,View才能显示在屏幕上DecorView作为顶级View,一般包括一个竖向的LinearLayout,在这个LinearLayout里面包括两部分,上面是标题栏,下面是内容栏。在Activity中通过setContentView就是把布局文件添加到内容栏中。DectorView其实是一个FrameLayout,View事件先经过DectorView,然后在传递给我们的ViewMeasureSpec(测量规格)MeasureSpec代表一个32位的int值,高2位代表SpecMode,低30位代表SpecSize,SpecModel代表测量模式,SpecSize是指某种测量模式下的规格大小。MeasureSpec通过将SpecModel和SpecSize打包成一个int值来避免过多的对象内存分配,为了方便操作,其提供打包和解包的方法。SpecModel和SpecSize也是一个int值,一组SpecModel和SpecSize可以打包成MeasureSpec,而一个MeasureSpec又可以解包为SpecModel和SpecSizeSpecModel的三大类:1. Unspecified父容器对View没有限制,一般用于系统内部,表示一种测量的状态2. Exactly父容器已经检测出View所需要的精确大小,此刻View的最终大小就是SpecSize所指的值。他应用于LayoutParams中的match_parent和具体的数值这两种模式3. At_Most父容器指定一个可用的大小即SpecSize,View的大小不能大于这个值。它对应于LayoutParams中的wrap_contentMeasureSpec和LauoutParams的对应关系在View测量的时候,系统会将LayoutParams在父容器的约束下转换为对应的MeasureSpec,然后在根据这个Measure来确定View测量后的宽高。LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View的宽高。对于DecorView来说,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定,从而得到View的宽高。对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LauoutParams来共同确定;对于普通的View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams确定的,MeasureSpec确定后,在OnMeasure方法中就可以确定View的宽高DectorView的MeasureSpec的创建过程width = getRootMeasureSpec(desiredWindowWidth,lp.width);height= getRootMeasureSpec(desiredWindowHeight,lp.height);开始测量  performMeasure(width,height);getRootMeasureSpec方法的实现MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.EXACTLY);DecorView产生MeasureSpec遵循以下原则(根据LayoutParams中的宽高划分):1. LayoutParams.MATCH_PARENT:精确模式,指窗口的大小2. LayoutParams。WRAP_CONTENT:最大模式,大小不定,但是不能超过窗体的大小3. 固定大小:精确模式,大小为LayoutParams中指定的大小View的measure过程总结:View的宽高由specSize决定的。直接继承View的自定义控件需要重写OnMeasure方法并设置wrap_content时的自身大小,否则在布局中使用Wrap_content就相当于在使用match_parent,因为View在布局中使用wrap_content时,specSize是AT_MOST模式,在这种模式下,他的宽高就等于specSize,而这种情况下SpecSize又是ParentSize(父容器可以使用的大小)ViewGroup的measure过程对于ViewGroup来说,除了完成自己的measure过程之外,还会遍历去调用所有子元素的measure方法,各个元素在递归去执行这个过程。和View不同的是,ViewGroup是一个抽象类,没有重写OnMeasure方法,但是他提供了一个measureCildren的方法,而onMeasure方法需要子类去实现,因为不同的ViewGrop子类有不同的布局特性,这导致他们的测量细节不同。如何在Activity启动的时候就获取View的宽高,不能再OnCreate方法中直接测量View的宽高,因为View的measure和Activity的生命周期是不同步的,解决方法如下:1. OnWindowFouceChanged:View已经初始化完毕了,宽高已经准备就绪,这时候在获取宽高,不过该方法会在Activity的获得焦点和失去焦点的时候都会被执行2. view.post(runnable):通过post将一个runnable投递到消息队列的尾部,然后等待Looper调用Runnable的时候,view已经初始化完毕,在进行测量3. ViewTreeObserver:使用ViewTreeObserver的众多回调可以完成此功能,比如使用OnGlobalLayoutListener这个接口,当View树的状态发生改变或者View树内部的View的可见性发生改变的时候,OnGlobalLayout方法将被回调4. view.measure(int widthMeasureSpec,intheightMeasureSpec):通过手动对View进行measure来得到View的宽高Layout过程作用:ViewGroup的作用是确定子元素的位置,当ViewGroup的位置被确定后,它在OnLayout中会遍历所有的子元素并调用其layout方法,在Layout方法中调用OnLayout方法又会被调用。Layout的大致流程:首先通过setFrame方法设定View的四个顶点的位置,接着调用onLayout方法,用于确定子元素的位置Drow过程作用:将View绘制到屏幕上。View的绘制过程遵循以下几步:(1)绘制背景background.draw(canvas)(2)绘制自己(onDraw)(3)绘制Children(dispatchDraw)(4)绘制装饰(OnDrawScrollBars)View的绘制过程是通过dispatchDraw来实现的,diaspatchDraw会遍历所有子元素draw方法。View有一个特殊的方法setWillNotDraw。如果View不需要绘制任何内容,那么设置这个标记为true以后,系统会进行相应的优化。默认情况下,View没有启用这个优化标记位,但是ViewGroup会默认启用这个优化标记位。这个标记位对实际开发的意义是:当我们的自定义控件继承于ViewGroup并且本身不具有绘制功能时,就可以开启这个标记位从而便于系统进行后续的优化。当然,当明确知道一个ViewGroup需要通过onDraw来绘制内容时,我们需要显示地关闭WILL_NOT_DRAW这个标记位   使用案例:以前我写过关于titlebar和bottom按钮切换操作的自定义view,因为在开发每个项目的时候,都会用到这两个东西,所以以前对这两块也特意花时间进行过一些自定义。首先创建类继承viewGroup,然后重写他的方法,可以根据自己的需求,设置自定义属性,然后初始化布局,将自定义的属性设置到布局当中,这样在使用这个自定义view的时候,可以通过xml引用的时候直接添加需要的属性。另外,为方便后期调用,一些使用频率比较高的属性,我特意进行了封装,便于在代码当中动态设置。这些view封装好以后,不但在新项目进行过程中,大大提高了开发效率,同时优化了代码编写,提高了代码阅读性。

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

推荐阅读更多精彩内容