可将整个绘制过程分为三部分:Measure - Layout - Draw 自定义ViewGrop onMeasure() onLayout() 自定义View则 onMeasure() onDraw()
自定义View的四个构造器
构造函数参数说明:
Context - 上下文;
AttributeSet - xml文件中的属性;
int defStyleAttr - Theme中的默认样式;
int defStyleResource - defStyleAttr未使用(为0,或者未匹配到),则应用于View的默认样式;
R.style中系统为view定义了很多默认主题Theme,主题中有对某些属性的默认赋值。
Measure 过程(自定义ViewGroup)
1. 先测量子视图再测量父视图
父视图会调用子视图的onMeasure(widthMeasureSpec ,heightmeasurespec) 子视图会调用子视图的onMeasure递归调用直到没有子视图结束调用.自定义View中重写的OnMeasure参数来自于父布局。
测量子视图通过getChildCound 获得子View个数逐个测量调用ChildView.setMeasure(childwidthMeasureSpec,childhetihtMeasureSpec),这个参数的来源不仅仅是LayoutParmes. 通过提供的getChildWidthMeasureSpec(父视图的Spec,padding,childview大小)计算得到
我们知道子视图的宽度高度在xml分为三种模式一种是确定大小的dp一种是warpcontent 一种是不确定的。通过child.getLayoutParmes获取每一个子视图的参数最后测量父视图通过setMeasuredDimension(width,height)设置值并保存里面的值如果父布局是Extially 从父布局的widthMeasureSpec 获取,否则就通过子view累加获得
Layout 过程
1. 在Layout过程中,子视图会调用getMeasuredWidth()和getMeasuredHeight()方法获取到measure过程得到mMeasuredWidth和mMeasuredHeight,作为自己的width和height。然后调用每一个子视图的layout(),来确定每个子视图在父视图中的位置。
layout(l ,r,t,b)涉及到view的坐标系 layout也是z子View先布局然后父view再布局.子view的left right bottom top都是通过子视图getMeasureWidth算出来 。getwidth()方法是onLayout之后才能调用, getmeasurewidth()则是通过测量之后setMeasureDimension()设置保存的
Draw 过程
1. dispatchDraw()发起对子视图的绘制,在View中默认为空实现,ViewGroup复写了dispatchDraw()来对其子视图进行绘制。
tip1.不要将初始化参数放到OnMeasure中因为OnMeasure多次调用容易内存抖动
tip2.自定义ViewGroup的 时候 整体的宽高会加上padding 子视图会去掉padding
tip3.测量的时候要判断子视图是否可见
tip4,onLyout的时候要考虑gravity