【Android】ScrollView嵌套Listview源码分析

从源码的角度分析Scrollview嵌套Listview显示一行问题,主要关注ScrollView和ListView的Measure过程,

1、ScrollView的OnMeasure()方法

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (!mFillViewport) {
    return;
}

final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED) {
    return;
}

if (getChildCount() > 0) {
    final View child = getChildAt(0);
 final int widthPadding;
 final int heightPadding;
 final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
 final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
 if (targetSdkVersion >= VERSION_CODES.M) {
        widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;
 heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;
 } else {
        widthPadding = mPaddingLeft + mPaddingRight;
 heightPadding = mPaddingTop + mPaddingBottom;
 }

    final int desiredHeight = getMeasuredHeight() - heightPadding;
 if (child.getMeasuredHeight() < desiredHeight) {
        final int childWidthMeasureSpec = getChildMeasureSpec(
                widthMeasureSpec, widthPadding, lp.width);
 final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                desiredHeight, MeasureSpec.EXACTLY);
 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
 }
}

2、FrameLayout onMeasure方法

int count = getChildCount();

final boolean measureMatchParentChildren =
        MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
        MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();

int maxHeight = 0;
int maxWidth = 0;
int childState = 0;

for (int i = 0; i < count; i++) {
    final View child = getChildAt(i);
 if (mMeasureAllChildren || child.getVisibility() != GONE) {
        measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);//重点关注
 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
 maxWidth = Math.max(maxWidth,
 child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
 maxHeight = Math.max(maxHeight,
 child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
 childState = combineMeasuredStates(childState, child.getMeasuredState());
 if (measureMatchParentChildren) {
            if (lp.width == LayoutParams.MATCH_PARENT ||
                    lp.height == LayoutParams.MATCH_PARENT) {
                mMatchParentChildren.add(child);
 }
        }
    }
}

// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
    maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
 maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}

setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
 resolveSizeAndState(maxHeight, heightMeasureSpec,
 childState << MEASURED_HEIGHT_STATE_SHIFT));

count = mMatchParentChildren.size();
if (count > 1) {
    for (int i = 0; i < count; i++) {
        final View child = mMatchParentChildren.get(i);
 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

 final int childWidthMeasureSpec;
 if (lp.width == LayoutParams.MATCH_PARENT) {
            final int width = Math.max(0, getMeasuredWidth()
                    - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                    - lp.leftMargin - lp.rightMargin);
 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                    width, MeasureSpec.EXACTLY);
 } else {
            childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
 getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                    lp.leftMargin + lp.rightMargin,
 lp.width);
 }

        final int childHeightMeasureSpec;
 if (lp.height == LayoutParams.MATCH_PARENT) {
            final int height = Math.max(0, getMeasuredHeight()
                    - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
                    - lp.topMargin - lp.bottomMargin);
 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                    height, MeasureSpec.EXACTLY);
 } else {
            childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
 getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                    lp.topMargin + lp.bottomMargin,
 lp.height);
 }

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
 }
}

需要重写 measureChildWithMargins。

再看Scrollview中measureChildWithMargins

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

测量模式为

MeasureSpec.UNSPECIFIED。交给子view去测量即Listview

3、listview onMeasure源码

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Sets up mListPadding
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);

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

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

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

如果父类测量规则是MeasureSpec.UNSPECIFIED,则取childHeight,而childHeight赋值是position为0的view的高度,所以,结果为只显示一个item

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值