在《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方法可以说是这个方法怎么使用的参考书,这个方法是ViewGroup用来对childView进行测量的时候调用:
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方法而已。