自定义View关于measure流程的基本思路整理

在《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的方法有两个

a)调用ViewGroup类的getChildMeasureSpec(int spec, int padding, int childDimension) 来获取View的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);  
     }            

   先获取ViewGroup的Spec即parentSpec,再加上childView的LayoutParams这两个信息,然后调用getChilMeasureSpec获取childView的Spec,既然获取到了Spec了,那么在调用View类的.measure方法把这个Spec交给measure方法处理就是了。

使用案例: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);

b)通过makeMeasureSpec(int size, int mode)方法来获取View的Spec值

该方法为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);
	
    }

所以上面的对View的测量总的来说其实是很简单的: 第一你只需要先获取View的Spec值,而且不需要只要具体的Spec得到的内部细节;第二,调用View的measure值把

得到的Spec传给measure方法即可。当然如果需要的话完全可以调用setMeasureDimension(int width,int height)来确定一个View的宽和高。具体测量的规则,不同的ViewGroup因为布局不同有着不同的测量逻辑,但是总的思路上是不会变化的,但是测量基本上会少不了Spec的计算就是了!

最后关于这个measure的灵活应用的使用案例可参考这篇博客,该篇博客主要就是运用了measureChildren方法对所有的ChildView进行测量,measureChildren方法其实也就是循环调用了measureChild方法而已。


你可能感兴趣的:(android)