[toc]
流式布局
自定义view与自定义viewgroup的区别
- 自定义view一般继承View等其他View,通常实现onMeasure与onDraw方法
- 自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承ViewGroup或各种Layout,通常实现onMeasure与onLayout
Android坐标系说明
两种坐标系
Android屏幕坐标系png.png
视图坐标系.png
MeasureSpec讲解
MeasureSpace是View的内部类,是int类型的,用高两位表示mode,低30位表示size
属性说明
- UNSPECIFIED 不对View大小进行限制,通常为系统使用,不用太关注
- EXACTLY 确切的大小,比如100dp
- AT_MOST 大小不可超过某个数值,比如 matchParent最大不能超过父布局
普通View的Measure的创建规则
通过表格快查子View的mode
普通View的Measure的创建规则
通过系统代码解析子View的MeasureSpec创建过程
一般是通过调用系统的 getChildMeasureSpec 获取子View的MeasureSpec,代码解析如下:
/**
* @param spec The requirements for this view(父元素ViewGroup的MeasureSpec)
* @param padding The padding of this view for the current dimension and margins, if applicable (父元素的padding值)
* @param childDimension How big the child wants to be in the current dimension(需要被测量的子View的) 属性值
* View.getLayoutParams().width/height
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// 获取父View的Mode与Size
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
// 计算父View留给子View的具体尺寸(去除padding值的尺寸)
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
// 判断父View的mode
switch (specMode) {
// Parent has imposed an exact size on us
// 如果父View是 EXACTLY (确定值)
// 子View为 具体尺寸 比如 100dp,则子view的mode为 EXACTLY,最大尺寸就是childDimension
// 子View为 MATCH_PARENT, 则子view的mode为 EXACTLY,最大尺寸为 size(父View留给子View的最大尺寸)
// 子View为 WRAP_CONTENT, 则子view的mode为 AT_MOST,最大尺寸为 size(父View留给子View的最大尺寸)
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
// 如果父View是 AT_MOST (大小与内容有关)
// 子View为 具体尺寸 比如 100dp,则子view的mode为 EXACTLY,最大尺寸就是childDimension
// 子View为 MATCH_PARENT, 则子view的mode为 AT_MOST,最大尺寸为 size(父View留给子View的最大尺寸)
// 子View为 WRAP_CONTENT, 则子view的mode为 AT_MOST,最大尺寸为 size(父View留给子View的最大尺寸)
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
// 此为系统调用,暂不分析 具体同上
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
// 计算子View的 MeasureSpec 并 返回
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
getWidth与getMeasureWidth区别
- getWidth是在onLayout以后才能获取到,是通过视图右边的坐标减去左边的坐标计算出来的
- getMeasureWidth是在onMeasure结束以后才可以获取到的值,是通过setMeasuredDimension设置的值
coding
补充说明
onMeasure调用次数
- onMeasure的调用次数与父View的算法有关系,比如:FrameLayout调用了子View的onMeasure有2次。