Я использую clip для обрезки изображения с помощью 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,
)
}
Установка ширины - правильный подход, вам просто нужно правильно 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 пикселей, в то время как поле с изображением во фрагменте ниже покрывает 200.dp, но не 200 пикселей. 200.dp — это 200px * плотность устройства.
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 painter = 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")
Эй, да, я могу объяснить. На вопрос уже был дан ответ с отлично работающим кодом, но вы все равно хотели что-то добавить, так что давайте посмотрим на это. Что делает этот длинный модификатор layout{} в вашем первом блоке кода? Я думаю, это то же самое, что и .width(58.dp), не так ли? Тогда есть пример компенсации бонуса. Хорошо, я думаю, но опять же, вы можете сделать это намного проще с выравниванием: Alignment { IntOffset(x, y) }. Я не хочу показаться грубым, ваши ответы в основном хороши, и я часто их поддерживаю, но здесь вы просто добавили запутанный ненужный код к уже работающему ответу IMO.
Это не потому, что ты ответил после меня, конечно. Я уже объяснил причину. Вы говорите, что «это устраняет необходимость создания формы» - мой ответ тоже. Ключом к этому вопросу был Alignment, и он уже был предоставлен. Вы добавили длинный блок .layout {}, который никому не нужен, и бонус смещения, что приятно, но можно сделать намного проще, без .layout {}. Опять же, это не я «нетерпим к альтернативам». Ваша альтернатива просто делает что-то очень простое (alignment = CenterStart) довольно сложным, поэтому, на мой взгляд, это не очень хороший ответ.
Для достижения одной и той же цели может быть много способов, и добавление всего 9 строк кода не делает ничего сложного с Modifier.layout{}. Вы можете сделать это разными способами. Вы можете использовать макет или холст, клип с прямоугольником или любой другой вариант, который вы предпочитаете. При всем уважении, только потому, что вы использовали выравнивание, это не превращает его в ключ. Честно говоря, я бы предпочел увидеть ответ на макет, а не что-то более распространенное в качестве альтернативы, поскольку вы сталкиваетесь с решениями с макетом реже, чем с чем-либо еще. Могут быть сложные или простые альтернативы, и люди могут выбирать из них.
Да, кажется, мы не согласимся здесь. Замена чего-то такого цельного, как Modifier.weight, на 9-строчную .layout{} определенно не нужна и запутана. "только потому, что вы использовали выравнивание" - вы тоже использовали его! Вот что заставляет ваш ответ работать - опять же, вы просто добавили что-то ненужное поверх него.
Извините, я не заметил, что вы убрали необходимость в пользовательской форме в своем ответе. Это должен быть принятый ответ. Спасибо