2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 自定义view onMeasure android测量模式

自定义view onMeasure android测量模式

时间:2021-10-15 23:03:46

相关推荐

自定义view onMeasure android测量模式

在自定义view中多半都会去重写onMeasure方法,进行view的测量,测量出大小后,再在onDraw方法中进行绘制,下面是一段简易的自定义view的代码:

public class MyTextView extends View {//在new一个MyTextView对象的时候会调用public MyTextView(Context context) {this(context,null);}//在xml布局文件中使用MyTextView会调用public MyTextView(Context context, AttributeSet attrs) {this(context, attrs,0);}//在xml布局文件中使用MyTextView并给MyTextView设置style样式会调用public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取宽高模式int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode=MeasureSpec.getMode(heightMeasureSpec);//获取宽高大小int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);}}

通过MeasureSpec.getMode()可以获取到宽高模式,系统提供了下面三个模式的常量值:

//在xml布局中设置为wrap_contentMeasureSpec.AT_MOST;//在xml布局中设置为具体的值比如100dp或者match_parent或者fill_parentMeasureSpec.EXACTLY;//在实际开发中很少用到,ScrollView、ListView等源码中有使用到MeasureSpec.UNSPECIFIED;

在项目开发中有时候会用到ScrollView和ListView的嵌套(当然现在很少用到了),就会碰到ListView显示条目不全的问题,其实就是ScrollView在测量时将ListView的测量模式设置为MeasureSpec.UNSPECIFIED,ListView在测量时的判断所导致的;ScrollView是一个布局容器,肯定是继承自ViewGoup的,在ViewGroup中会发现measureChild()方法,该方法是用来测量布局容器中子view的方法,在ViewGoup中的measureChild()方法中并没有指定子view的测量模式,

//这个是ViewGroup中的measureChild方法源码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);//调用view中的measure方法去测量子viewchild.measure(childWidthMeasureSpec, childHeightMeasureSpec);}

ViewGroup中没有做任何动作,但是ScrollView中重写了ViewGroup中的measureChild()方法,

//这是ScrollView中measureChild的源码,@Overrideprotected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {ViewGroup.LayoutParams lp = child.getLayoutParams();int childWidthMeasureSpec;int childHeightMeasureSpec;//在这里指定了子view的height mode 为MeasureSpec.UNSPECIFIEDchildWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft+ mPaddingRight, lp.width);final int verticalPadding = mPaddingTop + mPaddingBottom;childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),MeasureSpec.UNSPECIFIED);//进行子view的测量child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}

在这里首先要明白的是childMeasure(childeWidthMeasureSpec,childHeightMeasureSpec);中的两个参数childeWidthMeasureSpec和childHeightMeasureSpec;childeWidthMeasureSpec和childHeightMeasureSpec它是包含两部分的,前两位是mode,后30为是值(size);接下来就会调用view中的measure方法及onMeasure()方法,并把宽高值和宽高模式传入;但是ListView的话将View中的onMeasure方法进行了重写;

//这里是ListView中onMeasure方法源码@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// Sets up mListPaddingsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取宽高模式final int widthMode = MeasureSpec.getMode(widthMeasureSpec);final int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获取宽高大小int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int childWidth = 0;int childHeight = 0;int childState = 0;mItemCount = mAdapter == null ? 0 : mAdapter.getCount();if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED|| heightMode == MeasureSpec.UNSPECIFIED)) {final View child = obtainView(0, mIsScrap);// Lay out child directly against the parent measure spec so that// we can obtain exected minimum width and height.measureScrapChild(child, 0, widthMeasureSpec, heightSize);childWidth = child.getMeasuredWidth();childHeight = child.getMeasuredHeight();childState = combineMeasuredStates(childState, child.getMeasuredState());if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(((LayoutParams) child.getLayoutParams()).viewType)) {mRecycler.addScrapView(child, 0);}}//在这里对宽度模式进行了判断if (widthMode == MeasureSpec.UNSPECIFIED) {widthSize = mListPadding.left + mListPadding.right + childWidth +getVerticalScrollbarWidth();} else {widthSize |= (childState & MEASURED_STATE_MASK);}//在这里对高度模式进行了判断if (heightMode == MeasureSpec.UNSPECIFIED) {//如果高度的模式是MeasureSpec.UNSPECIFIED 计算的heightSize大小就是top+bottom+单个item条目高度(childHeight)+分割线的高度heightSize = mListPadding.top + mListPadding.bottom + childHeight +getVerticalFadingEdgeLength() * 2;}if (heightMode == MeasureSpec.AT_MOST) {// TODO: after first layout we should maybe start at the first visible position, not 0//如果高度模式是MeasureSpec.AT_MOST就会去计算所有item条目的高度,并赋值个heightSize heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);}//设置计算好的宽高setMeasuredDimension(widthSize, heightSize);mWidthMeasureSpec = widthMeasureSpec;}

在ScrollView和ListView嵌套的时候,ScrollView给ListView高度模式设置的是MeasureSpec.UNSPECIFIED,同时ListView又对onMeasure方法进行了重写,所以就出现了ScrollView嵌套ListView条目显示不全的问题,其实只需将ListView的高度模式设置为MeasureSpec.AT_MOST就可以去测量计算所有item的高度了;

public class MyListView extends ListView {public MyListView(Context context) {this(context,null);}public MyListView(Context context, AttributeSet attrs) {this(context, attrs,0);}public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//指定ListView的高度模式为MeasureSpecAT_MOST 并指定大小为Integer最大值右移两位heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, heightMeasureSpec);}}

这样子问题就解决了,宽高模式设置为MeasureSpec.AT_MOST容易理解,大小设置为Integer的最大值右移两位;

//这里是ListView中测量所有item高度的源码 maxHeight就是heightSizefinal int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,int maxHeight, int disallowPartialChildPosition) {final ListAdapter adapter = mAdapter;if (adapter == null) {return mListPadding.top + mListPadding.bottom;}// Include the padding of the list//定义的返回height变量int returnedHeight = mListPadding.top + mListPadding.bottom;final int dividerHeight = mDividerHeight;// The previous height value that was less than maxHeight and contained// no partial childrenint prevHeightWithoutPartialChild = 0;int i;View child;// mItemCount - 1 since endPosition parameter is inclusiveendPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;final AbsListView.RecycleBin recycleBin = mRecycler;final boolean recyle = recycleOnMeasure();final boolean[] isScrap = mIsScrap;for (i = startPosition; i <= endPosition; ++i) {child = obtainView(i, isScrap);measureScrapChild(child, i, widthMeasureSpec, maxHeight);if (i > 0) {// Count the divider for all but one childreturnedHeight += dividerHeight;}// Recycle the view before we possibly return from the methodif (recyle && recycleBin.shouldRecycleViewType(((LayoutParams) child.getLayoutParams()).viewType)) {recycleBin.addScrapView(child, -1);}returnedHeight += child.getMeasuredHeight();//maxHeight值是Integer.MAX_VALUE>>2为,所有returnedHeight的值是永远小于maxHeight的,这个if条件是永远不成立的,这样就可以返回returnedHeight计算出来的值,也就是设置height大小为Integer.MAX_VALUE>>2的原因if (returnedHeight >= maxHeight) {// We went over, figure out which height to return. If returnedHeight > maxHeight,// then the i'th position did not fit completely.return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)&& (i > disallowPartialChildPosition) // We've past the min pos&& (prevHeightWithoutPartialChild > 0) // We have a prev height&& (returnedHeight != maxHeight) // i'th child did not fit completely? prevHeightWithoutPartialChild: maxHeight;}if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {prevHeightWithoutPartialChild = returnedHeight;}}// At this point, we went through the range of children, and they each// completely fit, so return the returnedHeightreturn returnedHeight;}

这里涉及到java中的左移,右移运算符;

<<:左移运算符,num << 1,相当于num乘以2>>:右移运算符,num >> 1,相当于num除以2>>> :无符号右移,忽略符号位,空位都以0补齐

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。