Android develops ScrollView nested ListView to show only one row for reasons and Solutions

First look at the onMeasure() method in ScrollView. The reason why we first look for onMeasure() method is that there must be a measurement problem. In the onMeasure() method, we call the super.onMeasure() method first. When we click in, we can see the onMeasure method called in the husband layout.

measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

The measureChildWithMargins method is a method in ViewGroup, because all layouts are subclasses of ViewGoup and ScrollView is no exception, so it can be presumed that ScrollView overrides this method and causes problems. Search for measureChildWithMargins() method in ScrollView, and indeed there are:

@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;
        final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
                Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
                MeasureSpec.UNSPECIFIED);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

It is obvious that the height size of child HeightMeasureSpec is Math.max(), and the height mode is UNSPECIFIED.

Then look at the onMeasure() method in ListView. In the ListView method, there is such a judgment:

if (heightMode == MeasureSpec.UNSPECIFIED) {
        heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                    getVerticalFadingEdgeLength() * 2;
    }

Here we can find the key to the problem. ScrollView restricts the height of the child control as much as possible, which leads to the above if condition. Then the height of heightSize is only padding value plus the height of a child Height. So how to solve this problem? The most direct way is to find a way not to let the above judgment stand, let the following judgment stand:

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

Then you can override the onMeasure() method of ListView

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

 

 

Posted on Thu, 10 Oct 2019 09:30:01 -0700 by c_pattle