onLayout的作用:
决定子View在ViewGroup中的位置
onLayout使用范围
只针对ViewGroup,且必须实现
onLayout(boolean changed, int l, int t, int r, int b)内参数个代表什么?
示例图changed: 当前View的大小和位置改变了
l: 相对于父容器左边的距离
t: 相对于父容器上边的距离
r: 相对于父容器右边的距离
b: 相对于父容器下边的距离
layout() 和 onLayout()
layout() 和 onLayout() 。layout()方法用来确定View本身的位置,onLayout()方法则用来确定所有子元素的位置。View和ViewGroup中都有layout()和onLayout()两个方法,但两个类中都没有实现onLayout(),其原因和ViewGroup中没有onMeasure()方法是相同的:因为不同ViewGroup的子类对布局的要求不一样。
当我们自定义了一个ViewGroup的时候,会先确定这个ViewGroup的位置,然后,通过重写 onLayout() 方法,遍历所有的子元素并调用其 layout() 方法,在layout()方法中onLayout()方法又会被调用。ViewGroup就是通过这个过程,递归地对所有子View进行了布局。
示例代码:
/**
*当这个view和其子view被分配一个大小和位置时,被layout调用。
* @param changed 当前View的大小和位置改变了
* @param l 相对于父容器左边的距离
* @param t 相对于父容器上边的距离
* @param r 相对于父容器右边的距离
* @param b 相对于父容器下边的距离
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mAllViews.clear();
mLineHeight.clear();
// 当前ViewGroup的宽度
int width = getWidth();
int lineWidth = 0;
int lineHeight = 0;
// 获取每一行的view集合
List<View> viewList = new ArrayList<>();
/**
* 1、通过getChildCount,获取子View的个数view个数
*/
int childCount = getChildCount();
/**
* 2、遍历childCount,通过getChildAt获取到对应的view,并按每一行的viewList记录,记录在mAllViews内
*/
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = childView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
/**
* 如果换行则添加则把高度加入mLineHeight,把当前行的viewList加入到mAllViews,并viewList,lineWidth,lineHeight
* 如果不换行则记录宽度则直接添加当前控件的宽度到lineWidth内
*/
if (lineWidth + childWidth > width - getPaddingLeft() - getPaddingRight()) {
// 记录当前行的Views
mAllViews.add(viewList);
// 记录LineHeight
mLineHeight.add(lineHeight);
// 重置当前行的Views
viewList = new ArrayList<>();
// 重置宽
lineWidth = childWidth;
// 重置高
lineHeight = childHeight;
} else {
lineWidth += childWidth;
/**
* 取当前控件的高度和之前记录的高度的最大值
*/
lineHeight = Math.max(lineHeight, childHeight);
}
viewList.add(childView);
}
/**
* 处理最后一行
*/
mAllViews.add(viewList);
mLineHeight.add(lineHeight);
/**
* 3、遍历mAllViews,通过view.layout(lc, tc, rc, bc);确定各个子View的位置
*/
// 行的个数
int lineNum = mAllViews.size();
// view的初始位置
int left = getPaddingLeft();
int top = getPaddingTop();
for (int i = 0; i < lineNum; i++) {
viewList = mAllViews.get(i);
lineHeight = mLineHeight.get(i);
for (View view : viewList) {
if (view.getVisibility() == GONE) {
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc = lc + view.getMeasuredWidth();
int bc = tc + view.getMeasuredHeight();
Log.d("onLayout","getMeasuredHeight="+view.getMeasuredHeight());
Log.d("onLayout","getHeight="+view.getHeight());
view.layout(lc, tc, rc, bc);
left += view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
left = getPaddingLeft();
top += lineHeight;
}
}
getMeasuredWidth()、getMeasuredHeight()与getWidth()、getHeight()存在什么样的差别?
image.png image.png1、getMeasuredWidth、getMeasuredHeight经过measure测量之后才能获取到的。
而getWidth、getHeight是经过layout设置过view布局位置之后才能获取到。所以,在onLayout使用getWidth和getHeight是不能得到对应的宽高的。
2、通常情况下,getWidth、getHeight和getMeasureWidth、getMeasureHeight得到的值是一致的,但是layout的四个坐标点是可以随便设置的,不一定会根据measure测量的大小对应来设置,所以两种方式存在不等的情况。
具体源码分析请参考:
https://blog.csdn.net/wsq_tomato/article/details/80323719
网友评论