1、引言android
不少时候,当android系统控件不能知足咱们的业务需求时,咱们会考虑实现自定义view。而自定义View能够分为两种状况,一种是想实现View,另外一种是想实现ViewGroup。程序员
本文的介绍是自定义ViewGroup中所采用的一种常见方式是继承FrameLayout来实现需求。布局
2、FrameLayout的优点继承
熟练掌握anroid布局的程序员都知道,android系统提供了四种主要的布局形式,LinearLayout、RelativeLayout、GridLayout、FrameLayout。开发
其中LinearLayout与GridLayout对于复写不是很友好,由于它自己已经限定了布局方式是特殊话的,很难再进行改造。而RelativeLayout与FrameLayout的布局方式 相对的属性较多,用户的可控性大,同时,继承以后的可塑性更强。get
两种比较的话,RelativeLayout的实现更复杂一些,若是自己对其了解不够深刻的话,可能会遇到不少的问题。FrameLayout的布局理念和实现都相对简单,同时又比ViewGroup多了具体的测量和布局实现、。it
3、FrameLayout的测量策略io
1.FrameLayout的测量理念容器
所有子View都单独测量,选择最大的子View测量结果做为FrameLayout的测量大小bug
2.FrameLayout的测量步骤
(1)关于MeasureSpec【来自父view的限制】
理解MeasureSpec是测量的首要要求,由于onMeasure的两个参数就是widthSpec和HeightSpec。
下面介绍MeasureSpec的含义和做用:
a.measureSpec是一个int值,它的32位数据中,包含了两种信息,mode和size。它的高两位是mode,剩下30位是size。
其中mode可选的有三种 0 1 2分别是UNSPECIFIED、EXACTLY、AT_MOST。
UNSPECIFIED: 未指定,也就是没有规则
EXACTLY:限制了大小,大小在size字段
AT_MOST:限制了最大的大小,大小在size字段
须要注意的是,提供的mode只是父view给当前view测量的一个参考和创建,你能够听从也能够不听从。
好比提供了mode为EXACTLY,正常状况下咱们会尊重父view的意见,设置本身为对应的size(事实上默认view的测量结果也是这样的,可见view的getDefaultSize方法).
可是你也能够拒绝这种意见,设置本身的大小为想要的,可是你可能也会付出代价(在layout的时候父view并不给你足够的大小)。
因此,一切取决于你想作什么。
(2)关于LayoutParams【来自子View的请求】
若是你是一个view,你关注meaurespec很大程度上就能解决问题,可是若是你是一个ViewGroup。你的子view的LayoutParams也会影响到你的测量(由于你是一个容器,你须要尽量的保证子view可以达到满意的状态)。
而LayoutParams就是子view在测量或布局过程当中,所能提供给父view的请求信息,觉得父view为子view的measureChild过程提供参考(一样,父view也能够忽略它)。
LayoutParams最重要也是不可缺的两个属性是layout_width和layout_height。
经验丰富的开发者知道它能够有三种选择:
1. 一个肯定的大于0的dp值
2.MATCH_PARENT
3.WRAP_CONTENT
简要说明:
一个肯定的大于0的dp值:即子view期待父布局给本身这样一个大小
MATCH_PARENT:子view不知道本身的大小,可是但愿和父布局同样大
WRAP_CONTENT:子view也不知道本身的大小,它想测量本身完以后再作决定
此时,咱们想知道默认状况下ViewGroup是如何测量的,
事实上,ViewGroup的onMeasure方法是空的,可是它提供了一个方法,这是它建议的测量策略-ViewGroup的getChildMeasureSpec方法。
这个方法并无决定ViewGroup的大小,而是肯定了子View的measureSpec,而后在测量完子view后能够根据总体来决定ViewGroup自己的大小。
如今假设ViewGroup是须要测量的控件,parentMeasureSpec是父view提供的限定,即上面的(1),layoutparams是子View提供的建议。
这两个每一个都有三种状况,自由组合的话有九种可能性。读者可自行阅读这部分的代码,比较容易理解。
(3)最终大小的设定
ViewGroup须要将测量结果肯定下来(由于父布局会拿来决定本身的大小),因此须要有一个办法可以设置本身的大小,
这个方法是setMeasuredDimension。这个方法须要在onMeasure里面调用,因这个onMeasure方法执行完,可能会回到父view的测量方法中,它可能会很快用到。
一样,父view只须要执行child.getMeasureWidth等...方法来获取子view大小
(4)FrameLayout的解决方案
1.首先对全部的view都进行测量一遍,使用默认建议的测量方式,并考虑margin。(measurechildwithMargin) [若是你对layoutparams为matchparent的child是如何测量的有疑问,能够参考getChildMeasureSpec这个默认的处理方式]
2.找到layoutparams为match_parent的child,放到一个list里面。等非match_parent的测量完以后,注意此时framelayout的大小已经能够肯定,由于全部有大小想法的view都已经测量完毕,FrameLayout能够setMeasuredDimension了。
3.对于list里面的child,须要进行再次测量,由于以前match_parent可是具体大小未定,如今定下来后,改变child的measurespec为excatly,而后让子view正确的测量。
(5) 为何须要(4)中的3
须要对child再次测量,这是每一个viewGroup的责任,你应当至少须要对child进行一次测量(这样子view能够设置本身的measureWidth).这是一个健康的流程。
若是你不设置,那么在接下来layout的时候便获取不到子view的大小,那么布局没有参考,此时会比较为难,除非你肯定不须要子view的数据就能够从新布局它。
可是若是你的子view自己是viewGroup,那么它若是没有child的measureWidth,也很难走下去。
小结:测量的主要目的是让各个view都了解并设置自身的大小,以便在接下来的layout过程当中可以提供恰当的参考,因此,不用作多,也不要不作
4、使用FrameLayout自定义ViewGroup在测量方面能够作哪些操做
1.若是你不想某些view的测量结果影响FrameLayout的大小
事实上,你能够将FrameLayout的onMeasure方法,在继承的方法里从新写一遍,而后根据本身的逻辑调整,这是最简单也是最容易改的方法。但有时候想尽可能减小写代码的数量(代码多、bug就多).
2.不想拷贝代码
那么你只能经过继承measureChildWIthMargin想一想办法了,这样可能会产生意想不到的bug。
5、FrameLayout的布局策略
..待补充