美文网首页
AndroidScrollView嵌套ListView无法全部显

AndroidScrollView嵌套ListView无法全部显

作者: ryanxun | 来源:发表于2020-12-12 22:41 被阅读0次
嘿,今天的你过的还好吗,今天分享解决ScrollView嵌套ListView无法全部显示问题的经历

首先我们看一下ScrollView源码,发现他继承了FrameLayout,而FrameLayout下的onMeasure方法有如下代码

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
        //拿到它的子View然后循环去测量
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
        //调用父类的方法进行测量,重要方法因为Scrollview重写了这个方法所以出现的问题
            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);
                }
            }
        }
    }
}

很明显,就是循环查找自己的子View不是GONE的话就调用ViewGroup的measureChildWithMargins方法
发现ScrollView重写了measureChildWithMargins方法

@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;
    <!-//childHeightMeasureSpec的模式设置成了MeasureSpec.UNSPECIFIED
    final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
            Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
            MeasureSpec.UNSPECIFIED);
 
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

代码里ScrollView重写了measureChildWithMargins方法对他的childHeightMeasureSpec进行了设置.设置他的高度的测量模式heightMode为MeasureSpec.UNSPECIFIED.然后调用child.measure(childWidthMeasureSpec,childHeightMeasureSpec);方法

接下来我们在看看ListView的源码

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Sets up mListPadding
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //获得高度的模式
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int childHeight = 0
    //如果item数量大于0并且有个模式为--UNSPECIFIED执行此方法
    if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
            || heightMode == MeasureSpec.UNSPECIFIED)) {
        final View child = obtainView(0, mIsScrap);
        childHeight = child.getMeasuredHeight();
        //如果高度的模式为UNSPECIFIED执行,我们通过刚刚查看ScrollView的源码发现是这种模式所以此方法为true
    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);
 
}

这样可以清楚看出第一个判断是如果Item的总数大于0并且它的高或者宽有一个模式为MeasureSpec.UNSPECIFIED就会执行这个方法,正常情况下这个判断应该 为true.就是拿到它的一个item的高度childHeight = child.getMeasuredHeight();紧接着它判断它的高度的模式是否为MeasureSpec.UNSPECIFIED显然也是true所以他就是让它的整个的高度等于childHeight 最后完成setMeasuredDimension(widthSize,heightSize)

那就可以知道出现这个原因是在scrollView中ListView在OnMeasure阶段无法测出实际的高度,我们需要给他设置AT_MOST模式以支持很大的高度。这时候可以自定义一个MyListView 继承自Listview,然后重写onMeasure方法即可:

接下来是我的解决办法
1.创建一个MyListView让他继承ListView
2.重写onMeasure方法
3.调用MyListView

代码实现方法首先是MyListView

public class MyListView extends ListView {
    public MyListView(android.content.Context context, android.util.AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 设置不滚动
     * 重写onMeasure方法
     */
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);

    }
}

最后调用MyListView

<cn.ryanliu.mall.util.MyListView
            android:id="@+id/list_lv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

测试后结果ok,解决问题

没更新动态或者频繁更新动态的时候都是在认真生活

相关文章

网友评论

      本文标题:AndroidScrollView嵌套ListView无法全部显

      本文链接:https://www.haomeiwen.com/subject/triygktx.html