ScrollView Nested ListView Display Incomplete

I believe that many android developers will encounter ScrollView nested ListView in the previous development, ListView will only display one, when the development, only to find out how to solve the problem online, not to find out why only one, recently when looking at a Daniel's information, listen to him to know the reason, so record here;
ScrollView and ListView are inherited from ViewGroup, so they call onMeasure method to measure when drawing.

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

    }

In the onMeasure method, we can get the width-height pattern and the width-height value by getMode and getSize.

//Acquisition of breadth and height mode
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int heightMod = MeasureSpec.getMode(heightMeasureSpec);
 //Get control width and height values
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 int heightSize = MeasureSpec.getSize(heightMeasureSpec);

The defaults of width and height are: MeasureSpec.AT_MOST and MesureSpec. EXACTLY, MeasureSpec.UNSPECIFIED.

MeasureSpec.AT_MOST is equivalent to wrap_content in the layout file
 MeasureSpec.EXACTLY corresponds to match_parent, fill_parent, 100dp in the layout.
MeasureSpec.UNSPECIFIED is as large as possible

The pattern specified by ScrollView when drawing is MeasureSpec.UNSPECIFIED, so the pattern of getting width and height by MeasureSpec.getMode() in onMeasure method of ListView is MeasureSpec.UNSPECIFIED, and ListView makes judgment in onMeasure method.

@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);
        }
        //Here ListView makes a judgment based on the pattern obtained

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

If the pattern is MeasureSpec.UNSPECIFIED, specify the height of ListView as top + bottom + child Height + get Vertical Fading EdgeLength ()* 2; here child Height is the height of an item, that is, why only one item is displayed. See here, you will know that custom ListView overrides the onMeasure method of ListView and changes its pattern to MeasureS. Pec. AT_MOST mode, which measures the height of all items in ListView and draws it to display.

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) {
        //Solve the problem of incomplete display
        heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

When rewriting, change the heightMeasureSpec value and mode, specify the maximum value of Integer and move it two times to the right (Integer. MAX_VALUE> 2) and set the mode to MeasureSpec.AT_MOST; then you can measure the height of all item s, and here the problem of incomplete display of ScrollView nested ListView is solved. If there is something wrong in the above, please communicate.

Keywords: Android

Added by willc on Thu, 20 Jun 2019 02:46:36 +0300