Как преобразовать 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

Другие вопросы по тегам