Skip to content

Instantly share code, notes, and snippets.

@Mercandj
Last active April 13, 2023 09:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mercandj/2a34a6e98ac6de6a5a6535125b7c6ea2 to your computer and use it in GitHub Desktop.
Save Mercandj/2a34a6e98ac6de6a5a6535125b7c6ea2 to your computer and use it in GitHub Desktop.
**
* Size the content to match a specified aspect ratio.
*
* @param ratioWidth the desired width positive ratio
* @param ratioHeight the desired height positive ratio
*/
// Adapted from "Modifier.aspectRatio" in the file "androidx.compose.foundation.layout.AspectRatio.kt"
// inside the library "androidx.compose.foundation:foundation-layout:1.4.0"
@Stable
fun Modifier.aspectRatioReference(
ratioWidth: Float,
ratioHeight: Float,
reference: AspectRatioReference = AspectRatioReference.MIN_PARENT_WIDTH_PARENT_HEIGHT
) = then(
AspectRatioReferenceModifier(
ratioWidth = ratioWidth,
ratioHeight = ratioHeight,
reference = reference,
debugInspectorInfo {
name = "aspectRatioReference"
properties["ratioWidth"] = ratioWidth
properties["ratioHeight"] = ratioHeight
properties["reference"] = reference
}
)
)
private class AspectRatioReferenceModifier(
private val ratioWidth: Float,
private val ratioHeight: Float,
private val reference: AspectRatioReference,
inspectorInfo: InspectorInfo.() -> Unit
) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
init {
require(ratioWidth > 0) { "ratioWidth $ratioWidth must be > 0" }
require(ratioHeight > 0) { "ratioHeight $ratioHeight must be > 0" }
}
private val ratio = ratioWidth / ratioHeight
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val size = constraints.findSize()
val wrappedConstraints = if (size != IntSize.Zero) {
Constraints.fixed(size.width, size.height)
} else {
constraints
}
val placeable = measurable.measure(wrappedConstraints)
return layout(placeable.width, placeable.height) {
placeable.placeRelative(0, 0)
}
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
) = if (height != Constraints.Infinity) {
(height * ratio).roundToInt()
} else {
measurable.minIntrinsicWidth(height)
}
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
) = if (height != Constraints.Infinity) {
(height * ratio).roundToInt()
} else {
measurable.maxIntrinsicWidth(height)
}
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
) = if (width != Constraints.Infinity) {
(width / ratio).roundToInt()
} else {
measurable.minIntrinsicHeight(width)
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
) = if (width != Constraints.Infinity) {
(width / ratio).roundToInt()
} else {
measurable.maxIntrinsicHeight(width)
}
private fun Constraints.findSize(): IntSize {
val matchPARENTWidth = when (reference) {
AspectRatioReference.PARENT_WIDTH -> true
AspectRatioReference.PARENT_HEIGHT -> false
AspectRatioReference.MIN_PARENT_WIDTH_PARENT_HEIGHT -> maxWidth < maxHeight
AspectRatioReference.MAX_PARENT_WIDTH_PARENT_HEIGHT -> maxWidth > maxHeight
}
return if (matchPARENTWidth) {
IntSize(maxWidth, (maxWidth * ratioHeight / ratioWidth).roundToInt())
} else {
IntSize((maxHeight * ratioWidth / ratioHeight).roundToInt(), maxHeight)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
val otherModifier = other as? AspectRatioReferenceModifier ?: return false
return ratio == otherModifier.ratio &&
reference == other.reference
}
override fun hashCode(): Int =
ratio.hashCode() * 31 + reference.hashCode()
override fun toString(): String = "AspectRatioReferenceModifier(" +
"ratioWidth=$ratioWidth, " +
"ratioHeight=$ratioHeight, " +
"reference=$reference" +
")"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment