Как преобразовать Image Composable для соответствия 3 точкам касания в Compose
В настоящее время я играю со своим старым устройством Instant Lab и пытаюсь воссоздать части старого приложения в компоновке реактивного ранца.
Особенностью устройства является обнаружение 3 точек касания на экране, чтобы создать границу отображаемого изображения.
Я смог обнаружить 3 точки касания с помощью компоновки реактивного ранца и найти координаты (x, y) каждой точки касания:
Теперь я хотел бы отобразить свое изображение между этими точками касания. Я знаю, что мне нужно использоватьImage
Компонуемый для отображения. Но я не знаю, как применить правильное преобразование, чтобы отобразить композицию между этими тремя точками, используя вращение и абсолютное положение (?).
Ожидаемый результат:
Заранее спасибо за вашу помощь.
Редактировать:
Я попытался использовать пользовательскую форму, которую я применяю к поверхности со следующим компонуемым:
@Composable
private fun Exposing(pointersCoordinates: PointersCoordinates)
{
val exposureShape = GenericShape { _, _ ->
moveTo(pointersCoordinates.xTopLeft(), pointersCoordinates.yTopLeft())
lineTo(pointersCoordinates.xTopRight(), pointersCoordinates.yTopRight())
lineTo(pointersCoordinates.xBottomRight(), pointersCoordinates.yBottomRight())
lineTo(pointersCoordinates.xBottomLeft(), pointersCoordinates.yBottomLeft())
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
Surface(
modifier = Modifier.fillMaxSize(),
shape = exposureShape,
color = Color.Yellow,
border = BorderStroke(1.dp, Color.Red)
) {
Image(
modifier = Modifier.fillMaxSize(),
bitmap = viewModel?.bitmap?.asImageBitmap() ?: ImageBitmap(0, 0),
contentDescription = "photo"
)
}
}
}
Работает правильно :) Но лучше ли так?
2 ответа
Вам не нужно использовать . Подойдет обрезанный до круга и с серым фоном. Конечно, вам понадобитсяImage
чтобы отобразить фактическое изображение, на которое вы будете перетаскивать точки. Вот пример реализации:
val x by remember { mutableStateOf (Offset(...)) } // Initial Offset
val y by ...
val z by ...
// The image is occupying all of this composable, and these will be positioned ABOVE the image composable using order of positioning.
Layout(
content = {
Box(/*Copy From Later*/)
Box(...)
Box(...)
}
) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) } // Default Constraining.
layout(constraints.maxWidth, constraints.maxHeight){
measurables[0].place(x.x, x.y)
measurables[1].place(y.x, y.y)
measurables[2].place(z.x, z.y)
}
}
Разместите коробку так,
Box(
modifier = Modifier
.clip(CircleShape)
.size(5.dp) // Change per need
.pointerInput(Unit) {
detectDragGestures { change, _ ->
x = change.position // similarly y for second, and z for the third box
}
}
)
Это должно отслеживать/обновлять/позиционировать точки, куда бы вы их ни перетаскивали. Все точки индивидуально определяются их собственными держателями штатов. Вам нужно будет добавить этоpointerInput
логика для каждого , но, вероятно, было бы лучше, если бы вы просто создали одну функцию, которая будет вызываться на основеindex
, уникальный для каждогоBox
, но это не то, что нужно здесь освещать.
Поскольку вы можете получить Rect из точек касания, вы можете использовать Canvas или Modifier.drawWithContent{}.
Вырезать изображение и рисовать
Если вы хотите обрезать изображение на основе прямоугольника, вы можете проверить этот ответ. С режимами смешивания вы можете обрезать не только прямоугольник или фигуры, которые легко создать, но и формы, которые вы получаете из Интернета или изображений.Как обрезать или разрезать компоновку?
Другой подход к отсечению — использование функции clip() DrawScope, этот подход только отсечения до Rect.
Также вы можете использовать Modifier.clip() с пользовательской формой, чтобы обрезать ее по мере необходимости, как в этом ответе.
Рисовать без зажима
Если вы не хотите обрезать свое изображение, нарисуйте весь внутренний прямоугольник изображения, вы можете сделать это с помощьюdstOffset
с илиtranslate
с
@Composable
private fun DrawImageWithTouchSample() {
val rect = Rect(topLeft = Offset(100f, 100f), bottomRight = Offset(1000f, 1000f))
val modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectTapGestures {
// Tap here to get points
}
}
val image = ImageBitmap.imageResource(id = R.drawable.landscape5)
Canvas(modifier = modifier) {
// Clip image
clipRect(
left = rect.left,
top = rect.top,
right = rect.right,
bottom = rect.bottom
){
drawImage(image = image)
}
// Not clipping image
// drawImage(
// image = image,
// dstOffset = IntOffset(x = rect.left.toInt(), y = rect.top.toInt()),
// dstSize = IntSize(width = rect.width.toInt(), height = rect.height.toInt())
// )
//
translate(
left = rect.left,
top = rect.top + 1000
){
drawImage(
image = image,
dstSize = IntSize(width = rect.width.toInt(), height = rect.height.toInt())
)
}
}
}
Изображение сверху обрезаноclipRect
в то время как второй масштабируется, чтобы поместиться внутри прямоугольника из-заdstSize