這是以前常見(jiàn)的一個(gè)問(wèn)題喻括,現(xiàn)在可能很多人都不用ListView了邀杏。但是之前一直不是很清楚是什么原因。
為什么ListView只顯示一個(gè)Item的高度
首先看下正常情況下ListView的高度是怎么算的唬血⊥看下ListView的onMeasure()方法如何計(jì)算高的:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Sets up mListPadding
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
.......
if (heightMode == MeasureSpec.UNSPECIFIED) {
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;
}
正常情況下唤崭,高的Mode是不會(huì)是MeasureSpec.UNSPECIFIED,所以heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);不用看猜也能猜到是所以Item高的和泣特,但是現(xiàn)在不正常了 我么看下MeasureSpec.UNSPECIFIED時(shí)浩姥,是如何計(jì)算高的
heightSize = mListPadding.top + mListPadding.bottom + childHeight +
getVerticalFadingEdgeLength() * 2;
這個(gè)childHeight就是第一個(gè)Item的高度:
final View child = obtainView(0, mIsScrap);
// Lay out child directly against the parent measure spec so that
// we can obtain exected minimum width and height.
measureScrapChild(child, 0, widthMeasureSpec, heightSize);
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
所以當(dāng)ListView的高的Mode被設(shè)置為MeasureSpec.UNSPECIFIED時(shí),它的高度就是第一個(gè)Item的高度加上一些padding值等了状您。這個(gè)跟我們看到ScrollView嵌套ListView看到的現(xiàn)象是一樣。所以我們可以大膽猜測(cè)兜挨,ScrollView篡改了ListView高的Mode膏孟。 是這樣嗎?
事實(shí)就是這樣拌汇,ScrollView重寫(xiě)了measureChild和measureChildWithMargins方法柒桑,在測(cè)量自己子View的時(shí)候會(huì)將高的Mode改成UNSPECIFIED。
@Override
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);
}
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +
heightUsed;
final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
MeasureSpec.UNSPECIFIED);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
看到了嗎噪舀,所以嵌套后的ListVIew高的Mode已經(jīng)被改了
childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),
MeasureSpec.UNSPECIFIED);
這應(yīng)該就是這個(gè)現(xiàn)象最本質(zhì)魁淳,最正確的解釋。網(wǎng)上有各種解釋?zhuān)郧耙矝](méi)怎么看懂∮氤現(xiàn)在擼源碼順便過(guò)了下界逛,發(fā)現(xiàn)源碼真是個(gè)好東西。
如何解決這個(gè)問(wèn)題呢
其實(shí)解決這個(gè)問(wèn)題的方式有很多纺座,網(wǎng)上一搜各種都有息拜。但是知道產(chǎn)生這個(gè)現(xiàn)象的原因后,我覺(jué)得最簡(jiǎn)單也是最好的解決方式就是將ListView的Mode改回去就行了净响,改成AT_MOST少欺。
/**
* 解決ScrollView嵌套后顯示的高不正確問(wèn)題
* @author frc on 2018/3/25.
*/
public class FrcListView extends ListView {
public FrcListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightSpec =MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightSpec);
就是這么簡(jiǎn)單。