If you want to better understand any ViewGroup , a great place to start is analyzing the onMeasure() and onLayout() methods within it. For the uninitiated, these are the methods on any view that describe the size and position of that view and its children (in the case of ViewGroup ).

A quick tour through the onMeasure() of ConstraintLayout reveals the following high-level process:

Having any size views in the container automatically adds a second pass through the measure process (or most of it, anyway).

From the last post, you may remember that views inside a ConstraintLayout have unique measurement characteristics — match_parent doesn’t work and we can set a dimension to 0dp to cause it to be any size. Have a look at the following snippet from ConstraintLayout measurement:

…​ int childWidthMeasureSpec ; if ( width == 0 ) { childWidthMeasureSpec = getChildMeasureSpec ( parentWidthSpec , widthPadding , WRAP_CONTENT ); } else { childWidthMeasureSpec = getChildMeasureSpec ( parentWidthSpec , widthPadding , width ); } int childHeightMeasureSpec ; if ( height == 0 ) { childHeightMeasureSpec = getChildMeasureSpec ( parentHeightSpec , heightPadding , WRAP_CONTENT ); } else { childHeightMeasureSpec = getChildMeasureSpec ( parentHeightSpec , heightPadding , height ); } …​

For measurement purposes, any size is equivalent to wrap_content and other values (such as match_parent ) would be passed through un-checked. These measurements don’t take into account any constraints, so a match_parent view would generally just set its dimension to equal that of the parent container. Keep that in mind for just a moment.

I said before that size-dependent (i.e. any size) views are measure again after constraints are applied. The second measure pass looks something like this:

ConstraintWidget widget = …​; int widthSpec = MeasureSpec . makeMeasureSpec ( widget . getWidth (), MeasureSpec . EXACTLY ); int heightSpec = MeasureSpec . makeMeasureSpec ( widget . getHeight (), MeasureSpec . EXACTLY ); ViewGroup . LayoutParams lp = child . getLayoutParams (); if ( lp . width == WRAP_CONTENT ) { widthSpec = getChildMeasureSpec ( widthMeasureSpec , widthPadding , lp . width ); } if ( lp . height == WRAP_CONTENT ) { heightSpec = getChildMeasureSpec ( heightMeasureSpec , heightPadding , lp . height ); } child . measure ( widthSpec , heightSpec );

The second time around, which happens after the constraints are applied, any size views are re-measured to be exactly the width or height that the constraints system computed. The same process does not happen for a view marked as match_parent . This is important because many views rely on measurements to compute content placement. Gravity on a Button or TextView , for example, aligns the text based on the view’s measured size, not its actual layout position.

So why doesn’t match_parent work in ConstraintLayout ? Because the view bounds are computed against the applied constraints, but the view measurement does not account for the same constraints. This leads to odd behaviors in view content placement at runtime.