Клип изображения в Jetpack Compose
Я использую клип, чтобы обрезать изображение с помощью Compose, чтобы показать только левую часть изображения. Но он сохраняет ширину с пустым пространством. Как я могу обрезать нужную часть (отмечена красным)?
Я попытался установить собственную ширину для изображения, но она не работает должным образом.
Вот код, который я использую,
object CropShape : Shape {
override fun createOutline(
size: androidx.compose.ui.geometry.Size,
layoutDirection: LayoutDirection,
density: Density
): Outline = Outline.Rectangle(
Rect(Offset.Zero, androidx.compose.ui.geometry.Size((58 * density.density), size.height))
)
}
@Composable
private fun test(){
Image(
modifier = Modifier
.height(142.dp)
.clip(CropShape)
.padding(start = 20.dp, bottom = 20.dp)
.rotate(TiltedImageRotation)
painter = resourcePainter(R.drawable.image),
contentScale = ContentScale.FillHeight,
contentDescription = null,
)
}
2 ответа
Установка ширины — правильный подход, вам просто нужно правильноalignment
думаю - пользуйсяalignment = Alignment.CenterStart
чтобы увидеть начало вашего изображения, а не центр, как на втором изображении:
Image(
modifier = Modifier
.height(142.dp)
.width(58.dp)
.padding(start = 20.dp, bottom = 20.dp)
.rotate(TiltedImageRotation)
painter = resourcePainter(R.drawable.image),
contentScale = ContentScale.FillHeight,
alignment = Alignment.CenterStart,
contentDescription = null,
)
Если вы хотите выровнять с некоторым смещением, как предложено в другом ответе, это можно сделать с помощьюAlignment
тоже, и проще:
val density = LocalDensity.current
Image(
alignment = Alignment { _, _, _ ->
val xOffset = density.run { 58.dp.toPx() / 2 }.roundToInt()
IntOffset(x = -xOffset, 0)
}
)
Обрезка не меняет размеры составного объекта, а меняет, какая часть составного объекта отрисовывается. Без clip или clipToBounds вы можете нарисовать что угодно из Composable с помощью модификаторов рисования, даже если размер Composable равен нулю.
Как в примере ниже с формой размером 200 пикселей.
val shape = GenericShape { size: Size, layoutDirection: LayoutDirection ->
addRect(Rect(0f, 0f, 200f, 200f))
}
мы ограничиваем область рисования только 200 пикселями, в то время как Box with Image во фрагменте ниже покрывает 200.dp, но не 200 пикселей. 200.dp — это 200 пикселей * плотность устройства.
Row(modifier = Modifier.border(2.dp, Color.Blue)) {
Box(
modifier = Modifier
.clip(shape)
.clickable {
}
.size(100.dp)
.background(Color.Green)
) {
Image(
modifier = Modifier.size(100.dp),
painter = painterResource(id = R.drawable.landscape1),
contentScale = ContentScale.FillBounds,
contentDescription = null
)
}
Box(
modifier = Modifier
.size(100.dp)
.background(Color.Yellow)
)
}
Ограничение размера рисунка при рисовании только части может быть достигнуто любым способом, который не является идеальным или ключом к этому.
Вы можете сделать это, используяModifier.layout{}
измеряя полный размер Placeable и помещая его с размером для обрезки, например
modifier = Modifier
.clipToBounds()
.layout { measurable, constraints ->
val width = (58 * density).toInt()
val placeable = measurable.measure(
constraints
)
layout(width, placeable.height) {
placeable.place(0, 0)
}
}
А поскольку мы отсекаем область по ширине макета, у нас есть только составной элемент 58.dp, который рисует от (0,0) до желаемой позиции.
Если вы измеряете составной элемент с шириной, указанной выше, вам также необходимо изменить выравнивание = Alignment.TopCenter, поскольку изображение используетAlignment.Center
по умолчанию. Добавление выравнивания ко второму изображению устранит проблему.
Image(
modifier = Modifier.height(142.dp),
painter = painterResource(R.drawable.landscape1),
contentScale = ContentScale.FillHeight,
contentDescription = null,
)
Row(
modifier = Modifier.border(2.dp, Color.Green)
) {
Image(
modifier = Modifier
.layout { measurable, constraints ->
val width = (58 * density).toInt()
val placeable = measurable.measure(
constraints.copy(
minWidth = width,
maxWidth = width
)
)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
}
.height(142.dp),
painter = painterResource(R.drawable.landscape1),
contentScale = ContentScale.FillHeight,
contentDescription = null,
)
Text(text = "Some Text After Image")
}
Row(
modifier = Modifier.border(2.dp, Color.Blue)
) {
Image(
modifier = Modifier
.clipToBounds()
.layout { measurable, constraints ->
val width = (58 * density).toInt()
val placeable = measurable.measure(
constraints
)
layout(width, placeable.height) {
placeable.place(0, 0)
}
}
.height(142.dp),
painter = painterResource(R.drawable.landscape1),
contentScale = ContentScale.FillHeight,
contentDescription = null,
)
Text(text = "Some Text After Image")
}
Вы также можете использовать Canvas или Modifier.drawBehind для достижения того же результата. Следует учитывать, что мы обрезаем прямоугольник до желаемого размера при рисовании внутренней ширины, которая является шириной фактического Painter или Bitmap, в то время как мы получаем высоту из Composable.
val художник = PainterResource(R.drawable.landscape1)
Row(
modifier = Modifier.border(2.dp, Color.Yellow)
) {
Box(
modifier = Modifier
.width(58.dp)
.drawBehind {
clipRect(
left = 0f,
right = width
) {
with(painter) {
draw(size = Size(painter.intrinsicSize.width, size.height))
}
}
}
.height(142.dp),
)
Text(text = "Some Text After Image")