在《MeasureSpec的简单说明》这篇文章中对android中View的MeasureSpec测量方法做了详细的说明,今天这篇博文就Measure的测量做一个简单的总结。本文着重点在于怎么将android的一般测量规律应用到自己的自定义View中去,同时也算是给指定一个确定的思路。而对这些测量的内部细节不多做说明。
其实对于Measure要分两个具体的情况:
1)如果测量的是一个View,那么通过View这个类带的measure(int widthMeasureSpec, int heightMeasureSpec)方法就可以完成测量过程。
2)如果是ViewGroup的话,需要先完成childView的测量,这是一个递归的过程。根据childView宽和高在做一些其他的处理,比如ListView或者GridView的onMeasure方法。
正如上面所说,view的Measure方法,是调用measureint widthMeasureSpec,int heightMeasureSpec),注意里面的参数,是根据widthSpec/heightSpec来最终确定view的大小的。所说在我们自定义view如果需要测量的话需要先分别获取view的widthSpec和heightSpec(注:为了描述方便下文widthSpec和heightSpec统统用Spec来表示)。如《MeasureSpec的简单说明》所说,获取Spec的方法有两个
.通常来说第一个参数传递的是View的parentView的spec。ViewGroupmeasureChild方法可以说是这个方法怎么使用的参考书:
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); //执行child的measure流程 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
使用案例:ListView对ItemView进行测量的时候获取ItemView的widthSpec就这么个思路,具体在measureScrapChild这个方法里面,相关代码如下:
//获取itemView的LayoutParams LayoutParams p = (LayoutParams) child.getLayoutParams(); //非空判断 if (p == null) { p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); child.setLayoutParams(p); } //省略两行代码 //根据父spec+chid的layoutParams int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, mListPadding.left + mListPadding.right, p.width);
该方法为ViewGroup类的一个静态方法,事实上getChildMeasureSpec这个方法最后也就是调用了makeMeasureSpec!
使用案例:ListView进行测量的时候获取ItemView的heightSpec就是这么个思路,具体在measureScrapChild这个方法里面,相关代码如下:
int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { //根据高度和设定的measureSpec来获取高度spec childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); }通过a或者b说的两个方法获取到View的Spec值之后就可以调用view的measure方法进行view的测量了,具体是使用a还是b,这个倒是根据具体需求具体分析了,没有固定的要求,可根据需求灵活处理之。ListView关于ItemView的测量完整代码如下:
private void measureScrapChild(View child, int position, int widthMeasureSpec) { //获取itemView的LayoutParams LayoutParams p = (LayoutParams) child.getLayoutParams(); //非空判断 if (p == null) { p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); child.setLayoutParams(p); } //省略两行代码 //根据父spec+chid的layoutParams int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, mListPadding.left + mListPadding.right, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { //根据高度和设定的measureSpec来获取高度spec childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } //测量child的宽和个搞 child.measure(childWidthSpec, childHeightSpec); }
得到的Spec传给measure方法即可。当然如果需要的话完全可以调用setMeasureDimension(int width,int height)来确定一个View的宽和高。具体测量的规则,不同的ViewGroup因为布局不同有着不同的测量逻辑,但是总的思路上是不会变化的,但是测量基本上会少不了Spec的计算就是了!
最后关于这个measure的灵活应用的使用案例可参考这篇博客,该篇博客主要就是运用了measureChildren方法对所有的ChildView进行测量,measureChildren方法其实也就是循环调用了measureChild方法而已。