相关文章:
Android 知识点总结(目录) https://blog.csdn.net/a136447572/article/details/81027701
view绘制主要包括三个方面:
measure 测量组件本身的大小
layout 确定组件在视图中的位置
draw 根据位置和大小,将组件画出来
measure
绘制组件的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
重写onMeasure 是 会有两个参数 widthMeasureSpec与heightMeasureSpec 这连个参数是系统提供的控件高度的建议值但最终值需要通过setMeasuredDimension() 方法来设置
int widthMeasure = MeasureSpec.getSize(widthMeasureSpec);
int widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMeasure = MeasureSpec.getSize(heightMeasureSpec);
int heightMeasureMode = MeasureSpec.getMode(heightMeasureSpec);
widthMeasureSpec与heightMeasureSpec 值 都是一个组合尺寸,它是一个32位bit值,高两位是尺寸模式specMode,低30位是尺寸大小值
可以通过 MeasureSpec.getSize() 与 MeasureSpec.getMode()方法分别获取尺寸模式和具体值
MeasureSpec.EXACTLY 用户指定 match_parent 或大小 :layout_width="50dip"
MeasureSpec.AT_MOST 用户指定 android:layout_width="wrap_content"
MeasureSpec.UNSPECIFIED 是用户未指定尺寸,这种情况不多
在 ViewGroup中 需要通过 measureChildren(widthMeasureSpec,heightMeasureSpec); 来计算ViewGroup中所有子View 的大小 然后通过 getChildCount()获取数量并循环计算总宽高
measureChildren(widthMeasureSpec,heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i < count ; i++) {
View child = getChildAt(i);
//计算过程
if ( (childWidth + lineWidth) > widthMeasure ){
width = Math.max(lineWidth,childWidth);
height += lineHeight;
lineHeight = childHeight;
lineWidth = childWidth;
}else{
lineWidth +=childWidth ;
lineHeight = Math.max(lineHeight,childHeight);
}
if (i == count -1 ){
height += lineHeight ;
width = Math.max(width,lineWidth);
}
}
最后得到计算出来的值 通过 setMeasuredDimension
方法设置宽高值
setMeasuredDimension((widthMeasureMode == MeasureSpec.EXACTLY)
?widthMeasure:width,(heightMeasureMode==MeasureSpec.EXACTLY)
?heightMeasure:height);
在计算中如果用户指定margin值时 则需要我们单独计算并添加,需要重写generateLayoutParams
来获得准确的margin ,可以直接返回 new MarginLayoutParams(getContext(), attrs);
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
onLayout
确定组件在视图中的位置 如果自定义时 是必须重写的方法
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
}
在onLayout 中 与onMeasure 相同,需要通过 int count = getChildCount();
获取子View 数量 并通过循环设置每个view的layout
代码是其他自定义view的代码
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
int count = getChildCount();
int lineWidth = 0;
int lineHeight = 0;
int top = 0;
int left = 0;
for (int j = 0; j < count; j++) {
View child = getChildAt(j);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
if ((childWidth + lineWidth) > getMeasuredHeight()) {
top += lineHeight;
left = 0;
lineHeight = childHeight;
lineWidth = childWidth;
} else {
lineHeight = Math.max(lineHeight, childHeight);
lineWidth += childWidth;
}
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc = lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
child.layout(lc, tc, rc, bc);
left += childHeight;
}
}
draw
根据位置和大小,将组件画出来
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
int originY = 300;
int halfWaveLen = mItemWaveLength/2;
mPath.moveTo(-mItemWaveLength+dx,originY+dy);
for (int i = -mItemWaveLength;i<=getWidth()+mItemWaveLength;i+=mItemWaveLength){
mPath.rQuadTo(halfWaveLen/2,-50,halfWaveLen,0);
mPath.rQuadTo(halfWaveLen/2,50,halfWaveLen,0);
}
mPath.lineTo(getWidth(),getHeight());
mPath.lineTo(0,getHeight());
mPath.close();
canvas.drawPath(mPath,mPaint);
canvas.drawPath(path,mPaint1);
}
onDraw 中 主要是通过在canvas上进行绘制连显示图片
view 在绘制的时候会通过上面这三个方法来确定view 的大小,位置,和其中的内容 ,最终显示到界面上