解决GridView高度计算不正确
解决方案:
新建一个类继承GridView并重写onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);//不要删除,不要屏蔽
ListAdapter adapter = getAdapter();
Class<?> gridClass = GridView.class;
Class<?> absListView = gridClass.getSuperclass();
int measuredHeight = getMeasuredHeight();
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && adapter != null) {
int childCount = adapter.getCount();
int height = getPaddingTop() + getPaddingBottom();
int mColumnWidth = 0;
try {
Field mListPadding = absListView.getDeclaredField("mListPadding");
mListPadding.setAccessible(true);
Rect o = (Rect) mListPadding.get(this);
height += o.top + o.bottom;
} catch (Exception e) {
e.printStackTrace();
}
try {
Field columnWidth = gridClass .getDeclaredField("mColumnWidth");
columnWidth.setAccessible(true);
mColumnWidth = (int) columnWidth.get(this);
} catch (Exception e) {
e.printStackTrace();
}
int singleHeight;
int numColumns = getNumColumns();
for (int i = 0; i < childCount; ) {
singleHeight = 0;
for (int j = 0; j < numColumns && i < childCount; i++, j++) {
View child = adapter.getView(i, null, this);// getChildAt(j);
ViewGroup.LayoutParams p = child.getLayoutParams();
if (mColumnWidth != 0) {
measureChild(child, MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
} else {
int width = MeasureSpec.getSize(widthMeasureSpec);
int singleWidth = (width - getHorizontalSpacing() * (numColumns - 1)) / numColumns;
measureChild(child, MeasureSpec.makeMeasureSpec(singleWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}
int measuredHeight1 = child.getMeasuredHeight();
singleHeight = Math.max(singleHeight, measuredHeight1);
}
height += singleHeight + getVerticalSpacing();
}
setMeasuredDimension(getMeasuredWidth(), height > measuredHeight ? height : measuredHeight);
}
}
GridView中计算高度的代码片段:
...
final View child = obtainView(0, mIsScrap);
...
childHeight = child.getMeasuredHeight();
...
if (heightMode == MeasureSpec.AT_MOST) {
int ourSize = mListPadding.top + mListPadding.bottom;
final int numColumns = mNumColumns;
for (int i = 0; i < count; i += numColumns) {
ourSize += childHeight;
if (i + numColumns < count) {
ourSize += mVerticalSpacing;
}
if (ourSize >= heightSize) {
ourSize = heightSize;
break;
}
}
heightSize = ourSize;
}
系统的GridView在计算高度的时候为了提高效率,在GridView设置高度为wrap_content时高度计算默认第一行第一列的高度为基准高度计算GridView整体高度。如果出现第二列比第一列高度高则会出现高度计算不准确,必须通过上下滑动才能显示完整内容。
解决以上问题的方法就是遍历所有子view。并计算所有子View宽高。
高度计算有以下难点:
难点一:
高度需要加上mListPadding.top与mListPadding.bottom高度。mListPadding不能在子类中获取,需要通过反射获取。其声明在AbsListView中。
难点二:
测量子View宽高,在GridView中有mColumnWidth成员变量可以直接使用。但是必须通过反射获取。如果获取不到,做了一个兼容:
int singleWidth = (width - getHorizontalSpacing() * (numColumns - 1)) / numColumns;
getHorizontalSpacing() * (numColumns - 1) 对应 列间距 * (列数-1)得到空白区域宽度。
此处计算每个item最大宽度。当然有可能计算不准确。在此,measureChild()方法中宽度spec一般情况下不要用widthMeasureSpec。有可能导致高度计算不准确。
如有错误请指正。