Android 面试—ScrollView嵌套ListView会显示不全

1.问题

scrollView嵌套ListView会导致ListView只显示一行item的高度,显示不全。

2.原因

通过查看源码发现:

ScrollView和ListView的heightMeasureSpec都是MeasureSpec.UNSPECIFIED

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    // MeasureSpec由父级传进来,因为ScrollView为MeasureSpec.UNSPECIFIED,所以这里的heightMode == MeasureSpec.UNSPECIFIED

    final int widthMode = MeasureSpec.getMode( widthMeasureSpec);

    final int heightMode = MeasureSpec.getMode( heightMeasureSpec);

    int widthSize = MeasureSpec.getSize( widthMeasureSpec);

    int heightSize = MeasureSpec.getSize( heightMeasureSpec);

    int childWidth = 0;

    int childHeight = 0;

    int childState = 0;

    mItemCount = mAdapter == null ? 0 : mAdapter.getCount();

    if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) {

        final View child = obtainView(0, mIsScrap);

        measureScrapChild(child, 0, widthMeasureSpec, heightSize);

        childWidth = child.getMeasuredWidth();

        childHeight = child.getMeasuredHeight();

        childState = combineMeasuredStates( childState, child.getMeasuredState());

        if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(((LayoutParams) child.getLayoutParams()).viewType)) {

            mRecycler.addScrapView(child, 0);

        }

    }

    if (widthMode == MeasureSpec.UNSPECIFIED){

        widthSize = mListPadding.left + mListPadding.right + childWidth + getVerticalScrollbarWidth();

    } else {

        widthSize |= (childState & MEASURED_STATE_MASK);

    }

    // 因为ListView的模式也是MeasureSpec.UNSPECIFIED,所以导致进入if

    if (heightMode ==MeasureSpec.UNSPECIFIED){

        // 高度就为一个的高度childHeight 

        heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2;

    }

    if (heightMode == MeasureSpec.AT_MOST) {

        heightSize = measureHeightOfChildren( widthMeasureSpec, 0, NO_POSITION, heightSize, -1);

    }

    setMeasuredDimension(widthSize, heightSize);

    mWidthMeasureSpec = widthMeasureSpec;

}

根据源码可以发现,导致显示不全的原因是heightMode == MeasureSpec.UNSPECIFIED进入了:

if (heightMode == MeasureSpec.UNSPECIFIED) {

        // 高度就为一个的高度childHeight 

            heightSize = mListPadding.top + mListPadding.bottom + childHeight +

                    getVerticalFadingEdgeLength() * 2;

}

因此导致heightSize 被重新赋值为了一个childHeight 的高度。

3.解决方法

自己继承一个ListView重写onMeasure,通过MeasureSpec.makeMeasureSpec()方法来设置heightMode为MeasureSpec.AT_MOST。

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    // 将模式设置为MeasureSpec.AT_MOST

    heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

Integer.MAX_VALUE>>2 :因为MeasureSpec 一个值表示了两个值,最高两位代表模式,后面代表的是尺寸。而这边makeMeasureSpec传的第一个参数是给子级的最大尺寸,所以右移两位就是能给子级最大的高度,所以解决了显示不全的问题。

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/123835888
今日推荐