本文提供:
1.原理解析(不是源码解析,为了不使读者困惑)
2.实战演练
measure spec
简单讲一下,就是32位的int值,前2位用来存储Mode(UNSPECIFIED,EXACTLY,AT_MOST),后30位用来存储Size。
我们measure的核心就是:父view group的measure spec+子view的layout params=子view的measure spec
measure(分为两种1.view group 2.view)
view:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
可以看到,MeasureSpec参数是view group给你的,而这个MeasureSpec参数,view group中会替你去计算,先不用关注这一点。
你需要关注的点是:
当你给一个view设置wrap_content的时候,效果等同于match_parent!可是TextView,ImageView为什么在指定wrap_content的时候,没有出现这个现象?原因在于他们自身已经处理好了。所以我们来进行实战演练
@Override 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); int width = widthSpecSize; int height = heightSpecSize; if (widthSpecMode == MeasureSpec.AT_MOST) { width = 100; } if (heightSpecMode == MeasureSpec.AT_MOST) { height = 100; } setMeasuredDimension(width,height); }
代码很简单,意思就是如果是wrap_content,直接设置100sp!我们直接定死了100!
所以这里我们可以再思考下TextView的测绘!没有什么好说的了!他肯定是计算文字的宽高啊!可是文字的宽高又咋计算?
这里给你剧透一下
- if (heightMode == MeasureSpec.EXACTLY)
- {
- height = heightSize;
- } else
- {
- mPaint.setTextSize(mTitleTextSize);
- mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
- float textHeight = mBounds.height();
- int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
- height = desired;
- }
(else代表wrap_content情况)
这样一个api,你只需要调用getTextBounds就能把画出这一段字所需要的面积放到mBounds中(即我们预定义的矩形中)
这样你就可以根据矩形去获取文字的高了!宽也是同理!
view group:
他是这样的:
递归所有孩子,对每个孩子进行计算MeasureSpec的操作,传给孩子,孩子进行测量
测量自身
所以,我们只需要重点关注一下这个“计算”工作即可。
先给你一下伪代码
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) [
for(View child :所有子view) {
measureChild(child,int widthMeasureSpec, int heightMeasureSpec);
}
}
protected void measureChild(View child,int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
child.getLayoutParams
计算(parentMeasure,childLayoutParams)
child.measure(计算的结果)
}
这个计算,前面有说,就是
父view group的measure spec+子view的layout params=子view的measure spec
UNSPECIFIED,EXACTLY,AT_MOST的含义忘说了,其实是这样的:
UNSPECIFIED用于系统内部测量,可不用关注;
EXACTLY顾名思义,代表你的宽高已经确定了,对应match_parent和设置固定值比如100dp这样的情况。
AT_MOST,对应wrap_content,别忘了正常情况下wrap_content等同于match_parent即可。
我们根据父view group的measure spec和子view的layout params,可以得出如下结论
如果你的子view是固定值,那么毫无疑问,你的measure spec的mode为EXACTLY,size为child size,毋庸置疑。
如果你的子view是match_parent,那么也自然可以知道,你的大小肯定也是parent的大小,而你的mode肯定也得跟parent的一样!比如parent是EXACTLY,你也就是EXACTLY;parent是AT_MOST(即wrap_content),那你也是AT_MOST。
如果你的子view是wrap_content的,那么也自然可以得出,他是parentSize,不管什么情况下都是AT_MOST模式。
(这里再讲解下AT_MOST,其实你可以认为是包住的意思,如果你不指定,就是最大,如果你指定了,包住你指定的即可。默认 情况下系统是没有指定的,所以AT_MOST可以认为是尽可能的大。而且AT_MOST和EXACTLY最明显的区别就是,前者的大小是未确定的,后者是已确定的。理解了这句话你回过头去看,可能会很轻松的就理解了上面得出的结论)
好了,计算讲完了。讲父view group的自己的测量。
我们拿LinearLayout实战演练下,简单讲下思路:
其实也是按你的经验来!
肯定是先measure所有孩子
但是你懂的,他是排在一个方向的!
比如竖直
你需要计算竖直方向上的总长度吧?
总长度包含啥?自高,padding,margin
不就好了?
至于水平?你还用算?
至此,measure讲完!
layout
其实和measure一个套路啊!
你只需要重点关注两个方法,onLayout和layout。onLayout是确定所有子元素的位置,layout是确定子元素自身的位置。所以你有没有印象了,或许在你曾经自定义view的时候,layout(...)一下你就成功的放置好了你的位置。
再拿LinearLayout举例子。它是怎么放的?
线性放的,这其中会有一系列的规则,比如margin等。
我们先放放看!
比如加一个孩子进去。
首先measure吧!然后你在layout中就可以获取测量的宽高了!用getMeasuredWidth这个api就可以!那么你岂不是已经知道了!直接0,0 然后是0+width,0+height两个坐标!那么就定位好了。直接调用layout(0,0,0+width,0+height)即可!
如果有个top margin比如10又是怎样的情况?
直接layout(0,10,0+width,10+height)就可以了!就是这么简单!
所以如果你想自定义view group的话,指定自己的规则,根据宽高,自己去layout即可!
draw
最简单了!源码也简单,使用也简单!
源码:
1.绘制背景
2.绘制自己
3.绘制孩子
4.绘制装饰
使用:
这个api太多了,什么drawCircle,drawArc,draw。。。只是一个api的积累罢了!
好了,趁着手还热着,赶紧去看看鸿洋或者启舰的自定义view实战系列吧!!!从此自定义view在你眼里再也没有任何秘密!~