ScrollView ListView 嵌套问题源码理解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiaofengma/article/details/85684884

ScrollView 嵌套ListView 出现不兼容问题,有下面这种解决方案

自定义ListView 继承ListView 重写 onMeasure方法

public class MyListView extends ListView {
    public MyListView(Context context) {
        super(context);
    }

    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST) ;
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

这里要讲的是为什么要设置MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST)

下面看Scrollview 测量子视图的方法

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);
}

给子视图的heightMeasureSpec 设置了 UNSPECIFIED 参数

下面再看下ListView的测量


protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Sets up mListPadding
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    ...
    if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
            || heightMode == MeasureSpec.UNSPECIFIED)) {
        final View child = obtainView(0, mIsScrap);
        ...
        childWidth = child.getMeasuredWidth();
        childHeight = child.getMeasuredHeight(); 
        ...
    }
 
    if (heightMode == MeasureSpec.UNSPECIFIED) {
        //相当于 一个子item的高度 + padding + VerticalFadingEdgeLength() * 2
        //这样导致listView 显示只有差不多 一个item高度
        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;
}

看了上面的几处源码大家应该理解了ScrollView 嵌套ListView 布局的问题了吧

那我们设置的时候为什么要 Integer.MAX_VALUE>>2 右移两位呢?

widthMeasureSpec heightMeasureSpec : 会包含两个信息是一个32位的值,第一个信息是模式:2位 值:30位  

那为什么要使用Integer.MAX_VALUE

看ListView 测量子视图的高度


final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
        int maxHeight, int disallowPartialChildPosition) { 
    for (i = startPosition; i <= endPosition; ++i) {
        child = obtainView(i, isScrap);

        measureScrapChild(child, i, widthMeasureSpec, maxHeight);

        if (i > 0) {
            // Count the divider for all but one child
            returnedHeight += dividerHeight;
        }

        // Recycle the view before we possibly return from the method
        if (recyle && recycleBin.shouldRecycleViewType(
                ((LayoutParams) child.getLayoutParams()).viewType)) {
            recycleBin.addScrapView(child, -1);
        }

        returnedHeight += child.getMeasuredHeight();
          
        if (returnedHeight >= maxHeight) { //Integer.MAX_VALUE 防止进入这个if判断            return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
                        && (i > disallowPartialChildPosition) // We've past the min pos
                        && (prevHeightWithoutPartialChild > 0) // We have a prev height
                        && (returnedHeight != maxHeight) // i'th child did not fit completely
                    ? prevHeightWithoutPartialChild
                    : maxHeight;
        }

        if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
            prevHeightWithoutPartialChild = returnedHeight;
        }
    }

    // At this point, we went through the range of children, and they each
    // completely fit, so return the returnedHeight
    return returnedHeight;
}

看了这些,大家应该明白为什么这么做了吧

猜你喜欢

转载自blog.csdn.net/qiaofengma/article/details/85684884