Skip to main content

Measuring

Primitive should privide an implementation of LayoutBehavior interface to define how it should measure itself, based on the provided SizeConstraints.

The layout() function returns a PrimitiveLayoutResult object that contains the width and height of the content, and optionally any layout data.

There are a few of built-in LayoutBehavior implementations provided for most common use cases:

  • ExactSizeConstraintsLayoutBehavior - width and height are set based on the provided SizeConstraints.
  • FillLayoutBehavior - width and height are set based on the SizeConstraints if the constraints are bounded, otherwise uses default values which can be provided via constructor.
  • FixedSizeLayoutBehavior - width and height are set to values provided via constructor.
  • AspectRatioLayoutBehavior - width and height are set according to an aspect ratio and SizeConstraints. It will respect the intrinsic size of the component being measured.
  • EqualDimensionsLayoutBehavior - width and height are set respecting SizeConstraints and trying to keep width and height equal. This will only not guarantee equal width and height if the constraints don't allow for it.

There are utility methods defined as extension functions on the Size object that can be used to compute appropriate Size. It's also possible to access androidContext in the layout() method via LayoutScope.

note

layout() method can be called on any thread, and has the following characteristics:

  • it must not cause any side-effects
  • it is guaranteed to be called at least once, and can be called multiple times

Measuring in practice​

In principle, there are two different ways that measuring can be implemented:

Mathematical calculations using SizeConstraints​

SizeConstraints is an object that provides the minimum and maximum width and height available for a Component. SizeConstraints are provided by parent Component to a child component. A child Component should define its size within those constraints.

In practice, the SizeConstraints object is passed as a parameter of the layout() method and can be used to carry out mathematical calculations and compute the final measurements based on minWidth, maxWidth, minHeight, and maxHeight values, as shown in the following example code:

override fun LayoutScope.layout(sizeConstraints: SizeConstraints): PrimitiveLayoutResult {
return PrimitiveLayoutResult(
size =
if (!sizeConstraints.hasBoundedWidth && !sizeConstraints.hasBoundedHeight) {
Size(defaultSize, defaultSize)
} else {
Size.withEqualDimensions(sizeConstraints)
})
}
note

The actual computation logic that uses SizeConstraints values is in Size.withEqualDimensions helper function.

Creating a View, measuring it, and reading the measured sizes​

Alternatively, a View can be created that represents the mount content then call the View.measure() method on it directly. After View.measure() completes, the measured width and height can be retrieved by calling View.getMeasuredWidth() and View.getMeasuredHeight():

override fun LayoutScope.layout(sizeConstraints: SizeConstraints): PrimitiveLayoutResult {
// The height should be the measured height of EditText with relevant params
val editTextForMeasure: EditText =
AppCompatEditText(androidContext).apply {
setHint(hintText)
setText(initialText)
background =
getBackgroundOrDefault(
androidContext,
if (inputBackground === ColorDrawable(Color.TRANSPARENT)) background
else inputBackground)
}

editTextForMeasure.measure(sizeConstraints.toWidthSpec(), sizeConstraints.toHeightSpec())
val measuredWidth = max(sizeConstraints.minWidth, editTextForMeasure.measuredWidth)
val measuredHeight = max(sizeConstraints.minHeight, editTextForMeasure.measuredHeight)

// For width we always take all available space, or collapse to 0 if unspecified.
val width =
if (!sizeConstraints.hasBoundedWidth) 0 else min(sizeConstraints.maxWidth, measuredWidth)

return PrimitiveLayoutResult(width, measuredHeight)
}
note

When the width and height can be determined using mathematical calculations, it is preferred over creating a View because the calculations are much more lightweight.