Android Listview设置Header或Footer隐藏后Header或Footer占位问题?

在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就会解决这个问题

你可能感兴趣的:(Android Listview设置Header或Footer隐藏后Header或Footer占位问题?)