在Android开发中,会经常遇到给ListView添加自定义的HeaderView或FooterView,如果我们想隐藏HeaderView或FooterView的时候,一般会调用view的setVisibility方法设置属性值GONE,这时候发现View元素虽然正常的被隐藏了,但是view所占的高度却没有被改变,view所占的位置还是被显示了出来,从而出现一片空白区域
解决思路:
解决问题之前首先需要明白为什么会出现这个问题?
通过源码可以看到,在常用的ViewGroup,例如LinearLayout,在onMeasure方法中对每个childView执行measure前,都会判断childView的visibility是否为GONE,如果是GONE,则不对这个childView执行meaure操作,也就是这个childView的高度不会被计算到LinearLayout的高度中,下面是LinearLayout中onMeasure方法的代码片段:
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == View.GONE) {
i += getChildrenSkipCount(child, i);
continue;
}
。。。
但是view在measure自己的时候,并不会去判断自身的visibility是否为GONE,也就是只有在parentView中才会去判断,所以当对LinearLayout里面的一个子View设置Visibility为GONE时,这个View是不会被measure的,最终也是不会被显示的。
了解了常用ViewGroup是如何测量宽高的,再来看一下ListView里面是如何测量的,通过阅读源码可以看到如果ListView的widthMode、heightMode任意一个是unspecified时,就会调用measureScrapChild方法,如果都不是unspecified的话则会调用measureHeightOfChildren方法,而在这个方法内部中也会调用measureScrapChild方法,一起来看一下measureScrapChild方法源码:
private void measureScrapChild(View child, int position, int widthMeasureSpec) {
LayoutParams p = (LayoutParams) child.getLayoutParams();
if (p == null) {
p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
child.setLayoutParams(p);
}
p.viewType = mAdapter.getItemViewType(position);
p.forceAdd = true;
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
mListPadding.left + mListPadding.right, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
可以看出ListView在对ChildView执行meaure之前,并没有判断visibility为GONE的情况,当lpHeight > 0 时,说明在xml布局文件中给ListView设置过了一个尺寸值,所以不会出现问题,而在else里面,设置mode为UNSPECIFIED,目的就是为了让ChildView自己去测量大小,而ChildView在measure自己时是不会考虑visibility情况的,看到这里也就明白了为什么在设置HeaderView或FooterView的visibility为GONE时,会出现空白了!
解决方法:
弄明白了原因以后就知道该如何解决这个问题了,不能直接设置HeaderView或FooterView的visibility为GONE,而是需要在HeaderView或FooterView外层嵌套一层parentView,并且设置这个ParentView的layout_height="wrap_content",然后对里面的childView设置visibility为GONE或VISIBLE就会解决这个问题