How to force an image to draw outside of its boundaries in Jetpack Compose
Imagine a case when you need to put an image as in the picture below.
And not like this:
I've found three ways to achieve it. But only one suited me because of the limited time.
- The first is manually draw the image bitmap inside
Canvas
usingdrawImage
composable. But it requires an extra time. I did it, but my math calculations were okay only for my phone screen size, not universal. So I throw it away.
@Composable
private fun ImageWithoutTopBounds(@DrawableRes id: Int) {
val bitmap = ImageBitmap.imageResource(id = id)
Canvas(
modifier = Modifier
.fillMaxSize()
) {
val canvasWidth = size.width.toInt()
val canvasHeight = size.height.toInt()
val imageHeight = bitmap.height
val imageWidth = bitmap.width
// magic calculations
drawImage(
image = bitmap,
srcOffset = IntOffset(),
dstOffset = IntOffset(),
srcSize = IntSize(),
dstSize = IntSize(),
)
}
}
2. Another complex to math way is to use Image
with custom ContentScale
and offset
. I failed this one too :(
@Composable
private fun ImageWithoutTopBounds(@DrawableRes id: Int) {
val painter = painterResource(id = id)
val calculatedYOffset = ...// another magic
val calculatedXYScale = ...
Image(
modifier = Modifier
.fillMaxSize()
.offset(y = calculatedYOffset),
painter = painter,
contentScale = object : ContentScale {
override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
ScaleFactor(calculatedXYScale.x, calculatedXYScale.y)
}
)
}
3. And the last solution which suited me 100% is to use things out of the box - wrapContentXXX
with unbound
set to true
. Quote from docs:
Allow the content to measure at its desired size without regard for the incoming maximum constraints
While second parameter alignment
will anchor it, Top/Bottom
for wrapContentHeight
and Start/End
for wrapContentWidth
or mix of both when its wrapContentSize
.
@Composable
private fun ImageWithoutTopBounds(@DrawableRes id: Int) {
val painter = painterResource(id = id)
Image(
modifier = Modifier
.wrapContentSize(align = Alignment.BottomCenter, unbounded = true) // here it is
.align(Alignment.Center),
painter = painter,
contentScale = FixedScale(1f) // if your image too big
)
}
Here some other examples:
Cheers :)