自定义view流程的第一步,测量
onMeasure(),测量自己的大小,为正式布局提供建议(只是建议,用不用还要看onlayout()
测量过程通过measure方法实现,是View树的自顶向下的一次遍历,每个View在循环过程中将自己的尺寸向下传递,测量完成后所有的view都清楚自己的尺寸,
通过setMeasuredDimension(width, height);
设置给系统
获取当前view的位置有
// 获取Top位置
public final int getTop() {
return mTop;
}
// 其余如下:
getLeft(); //获取子View左上角距父View左侧的距离
getBottom(); //获取子View右下角距父View顶部的距离
getRight(); //获取子View右下角距父View左侧的距离
与MotionEvent中 get()和getRaw()的区别
view自身中心点为坐标原点
//get() :触摸点相对于其所在组件坐标系的坐标
event.getX();
event.getY();
//getRaw() :触摸点相对于屏幕默认坐标系的坐标
event.getRawX();
event.getRawY();
有关measure过程
测量规格(MeasureSpec) = 测量模式(mode) + 测量大小(size)
测量模式有三种:
模式 | 具体秒速 | 应用场景 | 备注 |
---|---|---|---|
UNSPECIFIED | 父视图不约束子视图,即view可取任意尺寸 | 系统内部recyclerview等 | |
EXACTLY | - 父视图为子视图制定一个确切尺寸 | match_parent或者100dp | 利用父View的剩余空间 |
AT_MOST | 父视图为子视图制定一个最大尺寸,子视图必须确保自身可适应在其内部 | wrap_content | 将大小设置为包裹view的内容 |
使用如下:
// 1. 获取测量模式(Mode)
int specMode = MeasureSpec.getMode(measureSpec)
// 2. 获取测量大小(Size)
int specSize = MeasureSpec.getSize(measureSpec)
// 3. 通过Mode 和 Size 生成新的SpecMode
int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);
//设置测量的宽高值
setMeasuredDimension(width, height);
下面是一个viewGroup中的测量:
/**
* 测量子view
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//每一行的高度
int lineWidth = 0;
//每一行的高度
int lineHeight = 0;
//整个viewGroup的宽度
int allWidth = 0;
//整个viewGroup的高度
int allHeight = 0;
//获得系统的建议宽高
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//获得控件的宽高模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//获得子view的数量
int count = getChildCount();
//遍历测量子view
for (int i = 0; i < count; i++) {
//获得子view对象
View childAt = getChildAt(i);
//测量子view,通过自view对象以及父view的测量参数
measureChild(childAt, widthMeasureSpec, heightMeasureSpec);
//获得子view的margin的layoutParams来获得子view的四周间距,兼容自定义viewGroup设置了margin
MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();
//获得子view测量的宽度
int childWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
//获得子view测量的高度
int childHeight = childAt.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
//如果现在的宽度加上一个字view的宽度大于整个viewGroup的宽度,那么就需要换行显示
if (lineWidth + childWidth > width) {
//换行后,刷新viewGroup的宽度,记录一个最大值
allWidth = Math.max(lineWidth, childWidth);
//记录viewGroup的高度
allHeight += lineHeight;
lineWidth = childWidth;
lineHeight = childHeight;
} else {
lineHeight = Math.max(lineHeight, childHeight);
lineWidth += childWidth;
}
//当时最后一行的时候,就不会换行,所以需要手动加上最后一行的高度
if (i == count - 1) {
allHeight += lineHeight;
//获得最大的宽度,比较值钱宽度的最大值,以及当前行的宽度值
allWidth = Math.max(lineWidth, allWidth);
}
}
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? width : allWidth, heightMode == MeasureSpec.EXACTLY ? height : allHeight);
}
网友评论