ScrollView嵌套 ListView显示不全

相信很多android开发者,在之前的开发中会碰到ScrollView嵌套 ListView的时候,ListView只会显示一条,当时开发的时候,只是在网上找了如何解决,并没有去弄清楚为什么会只显示一条,最近在看一位大牛的资料的时候,听他一讲也就知道了原因,所以记录在这里;
ScrollView和ListView都是继承自ViewGroup的,那他们在绘制的时候都会调用onMeasure方法进行测量;

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

在onMeasure方法里面我们可以通过getMode和getSize获取宽高的模式和宽高的值;

//获取宽高的模式
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int heightMod = MeasureSpec.getMode(heightMeasureSpec);
 //获取控件宽高值
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 int heightSize = MeasureSpec.getSize(heightMeasureSpec);

这里说下宽高的默认,经常使用的为:MeasureSpec.AT_MOST和MeasureSpec.EXACTLY,MeasureSpec.UNSPECIFIED的话使用的就比较少,

MeasureSpec.AT_MOST相当于布局文件中的wrap_content
MeasureSpec.EXACTLY相当于在布局中的match_parent、fill_parent、100dp
MeasureSpec.UNSPECIFIED尽可能的大

而ScrollView在绘制的时候指定的模式就是MeasureSpec.UNSPECIFIED,所以ListView中onMeasure方法中通过MeasureSpec.getMode()的时候获取宽高的模式就是MeasureSpec.UNSPECIFIED,而ListView在onMeasure方法中就做了判断

@Override
    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);
        }
        //在这里ListView根据获取到的模式做了判断

        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的时候,就指定ListView的高度为top+bottom +childHeight +getVerticalFadingEdgeLength() * 2;这里childHeight 就是一个item条目的高度,也就是为什么只显示一个条目了,看到这里,就知道了,自定义ListView重写ListView的onMeasure方法,将它的模式改变为MeasureSpec.AT_MOST模式,这样就会去测量ListView所有item条目的高度,并将它绘制显示出来;

public class MyListView extends ListView{

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

    public MyListView(Context context) {
        super(context);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //解决显示不全的问题
        heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

在重写的时候改变heightMeasureSpec值和模式,值指定为Integer的最大值并右移两位(Integer.MAX_VALUE>>2),模式设置为MeasureSpec.AT_MOST;这样子就可以测量所有item的高度了,到这里ScrollView嵌套 ListView显示不全的问题就解决了,如上面写的有不对的地方,欢迎交流。

你可能感兴趣的:(android)