ScrollView 嵌套ListView 出现不兼容问题,有下面这种解决方案
自定义ListView 继承ListView 重写 onMeasure方法
public class MyListView extends ListView { public MyListView(Context context) { super(context); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); } public MyListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST) ; super.onMeasure(widthMeasureSpec, heightMeasureSpec); }}复制代码
这里要讲的是为什么要设置MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST)
下面看Scrollview 测量子视图的方法
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { ViewGroup.LayoutParams lp = child.getLayoutParams(); int childWidthMeasureSpec; int childHeightMeasureSpec; childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int verticalPadding = mPaddingTop + mPaddingBottom; childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec( Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding), MeasureSpec.UNSPECIFIED); child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}复制代码
给子视图的heightMeasureSpec 设置了 UNSPECIFIED 参数
下面再看下ListView的测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Sets up mListPadding super.onMeasure(widthMeasureSpec, heightMeasureSpec); ... if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0, mIsScrap); ... childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); ... } if (heightMode == MeasureSpec.UNSPECIFIED) { //相当于 一个子item的高度 + padding + VerticalFadingEdgeLength() * 2 //这样导致listView 显示只有差不多 一个item高度 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 heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1); } setMeasuredDimension(widthSize, heightSize); mWidthMeasureSpec = widthMeasureSpec;}复制代码
看了上面的几处源码大家应该理解了ScrollView 嵌套ListView 布局的问题了吧
那我们设置的时候为什么要 Integer.MAX_VALUE>>2 右移两位呢?
widthMeasureSpec heightMeasureSpec : 会包含两个信息是一个32位的值,第一个信息是模式:2位 值:30位
那为什么要使用Integer.MAX_VALUE呢
看ListView 测量子视图的高度
final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, int maxHeight, int disallowPartialChildPosition) { 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 child returnedHeight += dividerHeight; } // Recycle the view before we possibly return from the method if (recyle && recycleBin.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { recycleBin.addScrapView(child, -1); } returnedHeight += child.getMeasuredHeight(); if (returnedHeight >= maxHeight) { //Integer.MAX_VALUE 防止进入这个if判断 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 returnedHeight return returnedHeight;}复制代码
看了这些,大家应该明白为什么这么做了吧