標籤:
常用的ViewGroup,例如LinearLayout,在onMeasure方法內對每個child view執行measure前,會判斷child view的visibility是否為gone。如果是gone,則不對這個child view執行measure操作,即這個child view的高度不被計算在linearLayout的高度裡面。LinearLayout的measureVertical程式碼片段:
if (child.getVisibility() == View.GONE) { i += getChildrenSkipCount(child, i); continue;}
view在measure自己時,並不會去判斷自己的Visibility是GONE。這個邏輯操作如上述代碼所示,是在parent view裡面做的。所以當對LinearLayout裡面的一個childView設定Visiblility為gone時,這個view不會被measure,最終也不會被顯示出來。
在使用ListView時,經常會添加一些headerView、footerView。但是當設定headerView、footerView的visibility為gone時,卻發現headerView、footerView雖然沒有顯示出來,但垂直方向其所佔的位置還是被顯示出來了,從而出現了空白地區。網上查到的解決辦法是:不能直接設定headerView、footerView的Visibility為gone。而是要在HeaderView、FooterView外麵包一層parent view(FrameLayout RelativeLayout 都可以),並設定layout_height=“wrap_content”。然後對裡面的childView設定visibility為GONE、VISIBLE都會生效。查看源碼,情況確實如此。
ListView的onMeasure裡面,如果ListView的widthMode、heightMode有一個是unspecified時(應該對應於在XML中沒有對listView設定layout_width、layout_height),會調用方法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在對child view執行measure前,沒有判斷visibility為gone的情況。
再看看裡面的詳細邏輯:
- lpHeight>0時,說明在xml或者程式裡面設定了一個確定的尺寸,這裡沒有問題;
- else裡面,設定mode為UNSPECIFIED,就是讓child view自己去決定大小,child view在measure自己時,不會考慮VISIBILITY屬性。
else包含的邏輯:
- lpHeight == 0:對應於在xml或者代碼中設定尺寸為0,說明也是不起作用的,最終還是會顯示出空白
- lpHeight == -1:MATCH_PARENT
- lpHeight == -2:WRAP_CONTENT
三種情況,都會根據view實際的內容返回一個wrap_content的尺寸。這裡就解釋了為何設定headerView、footerView的visibility為gone時,會出現空白地區。
如果外麵包一層parent view(例如LinearLayout),並設定layout_height為wrap_content(按上面的分析,設定match_parent也是可以的),listView會調用調用額外的這個parent view的measure方法。而LinearLayout在measure時,會判斷child view的visibility,如果為gone,則會返回0.最終這個額外的parent view返回給list view的尺寸就是0,從而解決了空白地區的問題。
這個問題算是listview的一個feature吧!
Android ListView的header footer設定visibility gone不起作用