自定义View - 三大流程 - 学习小结

本文详细探讨了自定义View的三大流程:测量、布局和绘制。在测量阶段,讲解了MeasureSpec的生成规则和测量过程,强调单一View与ViewGroup的差异。布局部分阐述了如何确定View及其子元素的位置。最后,讨论了绘制过程,并提到了RecycleView的ItemDecoration。文章还提及自定义View时对wrap_content和padding的支持,以及在特定情况下可能需要的多次测量。
摘要由CSDN通过智能技术生成

学习资源来源:https://www.jianshu.com/nb/9976005

measure、draw均为调度方法。具体的施工由onMeasure、onDraw实现。

  • layout方法确定View本身的位置,而onLayout方法会确定所有子元素的位置;因此,对于单一View的layout过程:由于单一View是没有子View的,故onLayout()是一个空实现,具体布局实现在layout中。   
  • 对于ViewGroup的layout过程:由于确定位置与具体布局有关,所以onLayout()在ViewGroup为1个抽象方法,需重写实现。
  • 自定义View需要支持padding和wrapcontent;而margin是由父容器控制的,默认已被支持。即外边距轮不到view来算,Andorid将其封装在LayoutParams内交由父容器统一处理。

测量 

MeasureSpec的生成规则

子View的LayoutParams  + 父View给出的测量规格(MeasureSpec) =  子View的测量规格。

即:子View的大小由父View的MeasureSpec和子View的LayoutParams 共同决定。

通过MeasureSpec的静态方法即可获取对应的Mode、Size以及生成新的MeasureSpec。

测量流程:

单一View的measure过程

                                                                              View的onMeasure实现

getSuggestedMinimumWidth:设置了背景图,则为背景图的大小,否则为android:minWidth的属性值。

setMeasuredDimension保存测量后的尺寸。ViewGroup可以通过child.getMeasuredWidth获取子View的测量值;

由以上代码可以看出,View本身是不支持wrap_content属性的,因此如果自定义View,需要完成对wrap_content的支持。

支持wrap_content及padding示例 - MeasureSpec使用示例:

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
		/**
		 * 设置宽度
		 */
		int specMode = MeasureSpec.getMode(widthMeasureSpec);
		int specSize = MeasureSpec.getSize(widthMeasureSpec);
 
		if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
		{
			Log.e("xxx", "EXACTLY");
			mWidth = specSize;
		} else
		{
			// 由图片决定的宽
			int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth();
			// 由字体决定的宽
			int desireByTitle = getPaddingLeft() + getPaddingRight() + mTextBound.width();
 
			if (specMode == MeasureSpec.AT_MOST)// wrap_content
			{
				int desire = Math.max(desireByImg, desireByTitle);
				mWidth = Math.min(desire, specSize);
				Log.e("xxx", "AT_MOST");
			}
		}
 
		/***
		 * 设置高度
		 */
 
		specMode = MeasureSpec.getMode(heightMeasureSpec);
		specSize = MeasureSpec.getSize(heightMeasureSpec);
		if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
		{
			mHeight = specSize;
		} else
		{
			int desire = getPaddingTop() + getPaddingBottom() + mImage.getHeight() + mTextBound.height();
			if (specMode == MeasureSpec.AT_MOST)// wrap_content
			{
				mHeight = Math.min(desire, specSize);
			}
		}
		setMeasuredDimension(mWidth, mHeight);
 
	}

在单一View measure过程中,getDefaultSize()只是简单的测量了宽高值,在实际使用时有时需更精细的测量。所以有时候也需重写onMeasure()。

关于测量宽高:

  1. 在某些情况下,需要多次测量(measure)才能确定View最终的宽/高;
  2. 该情况下,measure过程后得到的宽 / 高可能不准确;
  3. 此处建议:在layout过程中onLayout()去获取最终的宽 / 高

ViewGroup的测量过程

遍历所有的子View的尺寸;

合并所有子View的尺寸,最终得到ViewGroup父视图的测量尺寸;

注:ViewGroup无法对onMeasure作统一实现,因为不同的ViewGroup子类有不同的测量规则。这也是单一View的measure过程和ViewGroup的measure过程最大的不同。

除了measureChildren,如下方式也是ViewGroup提供的测量相关的方法。

布局

视图的位置即四个顶点位置:Left、Top、Right、Bottom。

单一View的布局过程

通过setFrame设置了View本身的位置。

注:对于单一View的laytou过程
  *    a. 由于单一View是没有子View的,故onLayout()是一个空实现
  *    b. 由于在layout()中已经对自身View进行了位置计算,所以单一View的layout过程在layout()后就已完成了

ViewGroup的布局过程

1、计算自身ViewGroup的位置:layout;

2、遍历View & 确定自身子View在ViewGroup的位置(调用子View的layout):onLayout;

 

绘制

详见”自定义View温习笔记

 

强行插播 - 关于RecycleView的ItemDecoration的

getItemOffsets

onDrawOver

一图胜千言,结束。

小知识点:

当包含一个View的Activity退出或者当前的View被remove时,View的onDetachedFromWindow方法会被调用。

 对应的方法onAttachedToWindow是当包含此View的Activity启动时,此方法被调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值