Android Jetpack Compose: как увеличить изображение в коробке?

Я собираюсь создать масштабируемое изображение внутри рамки, как на первом снимке экрана. Но когда я увеличиваю изображение, оно выходит из коробки. Есть ли способ увеличить изображение, но сохранить размер? Без view или fragment только box кажется недостаточно. Я ожидаю, что изображение станет больше, но все еще останется внутри красного прямоугольника, но я получил второй снимок экрана после увеличения.

Перед увеличением После увеличения

Благодаря nglauber и Amirhosein я получил окончательное решение с одновременным использованием функций масштабирования и перетаскивания внутри поля (фиксированная область) со следующим кодом в качестве нового снимка экрана, как показано ниже.

        val imageBitmap = imageResource(id = R.drawable.android)
        Image(
            modifier = Modifier
                .preferredSize(400.dp, 300.dp)
                .clip(RectangleShape)
                .zoomable(onZoomDelta = { scale.value *= it })
                .rawDragGestureFilter(
                    object : DragObserver {
                        override fun onDrag(dragDistance: Offset): Offset {
                            translate.value = translate.value.plus(dragDistance)
                            return super.onDrag(dragDistance)
                        }
                    })
                .graphicsLayer(
                    scaleX = scale.value,
                    scaleY = scale.value,
                    translationX = translate.value.x,
                    translationY = translate.value.y
                ),
            contentDescription = null,
            bitmap = imageBitmap
        )

введите здесь описание изображения


person joyl1216    schedule 02.02.2021    source источник
comment
Где вы можете ограничить translateX и translateY, чтобы изображение оставалось внутри поля?   -  person Bugdr0id    schedule 13.07.2021
comment
Modifier.clipToBounds() или clip(RectangleShape) подошли бы @Bugdr0id   -  person joyl1216    schedule 29.07.2021


Ответы (3)


Вот мое решение... Может кому пригодится...

@Composable
fun ZoomableImage() {
    val scale = remember { mutableStateOf(1f) }
    val rotationState = remember { mutableStateOf(1f) }
    Box(
        modifier = Modifier
            .clip(RectangleShape) // Clip the box content
            .fillMaxSize() // Give the size you want...
            .background(Color.Gray)
            .pointerInput(Unit) {
                detectTransformGestures { centroid, pan, zoom, rotation ->
                    scale.value *= zoom
                    rotationState.value += rotation
                }
            }
    ) {
        Image(
            modifier = Modifier
                .align(Alignment.Center) // keep the image centralized into the Box
                .graphicsLayer(
                    // adding some zoom limits (min 50%, max 200%)
                    scaleX = maxOf(.5f, minOf(3f, scale.value)),
                    scaleY = maxOf(.5f, minOf(3f, scale.value)),
                    rotationZ = rotationState.value
                ),
            contentDescription = null,
            painter = painterResource(R.drawable.dog)
        )
    }
}
person nglauber    schedule 03.02.2021
comment
У меня это работает, даже помещая его в Column или другой макет. Спасибо! - person joyl1216; 03.02.2021
comment
.clip(RectangleShape) это ключевой момент, который я упускаю! Ваша версия предназначена только для масштабирования, но не для перетаскивания. - person joyl1216; 04.02.2021

Просто установите zoomable и rawDragGestureFilter на Image вместо Box :

@Preview
@Composable
fun Zoomable(){
val scale = remember { mutableStateOf(1f) }
val translate = remember { mutableStateOf(Offset(0f, 0f)) }

Box(
    modifier = Modifier.preferredSize(300.dp)

) {
    val imageBitmap = imageResource(id = R.drawable.cover)
    Image(
        modifier = Modifier
            .zoomable(onZoomDelta = { scale.value *= it })
            .rawDragGestureFilter(
                object : DragObserver {
                    override fun onDrag(dragDistance: Offset): Offset {
                        translate.value = translate.value.plus(dragDistance)
                        return super.onDrag(dragDistance)
                    }
                })
            .graphicsLayer(
                scaleX = scale.value,
                scaleY = scale.value,
                translationX = translate.value.x,
                translationY = translate.value.y
            ),
        contentDescription = null,
        bitmap = imageBitmap
    )
  }
}
person Amirhosein    schedule 02.02.2021
comment
Спасибо за ваш ответ, но он по-прежнему работает с вашим изменением, выходя из коробки при увеличении. - person joyl1216; 02.02.2021
comment
Вы устанавливаете статический размер для Box? - person Amirhosein; 03.02.2021
comment
Да, я даже копирую ваш код в свой проект, но он все равно работает. У вас работает масштабирование, оставаясь внутри коробки? - person joyl1216; 03.02.2021
comment
Я все равно использую Jetpack Compose 1.0.0-alpha11. - person joyl1216; 03.02.2021
comment
Я понимаю, почему. Я поместил Box внутрь Column, поэтому он перестал работать. Если просто коробка как root макет, то работает. Но по-прежнему не имеет смысла ломать Box только из-за того, что он помещается внутри другого макета. - person joyl1216; 03.02.2021
comment
Затем удалите поле (если вы не используете какие-либо функции, которые оно предоставляет) и установите статический размер для Image. У меня работает (1.0.0-alpha11) - person Amirhosein; 03.02.2021
comment
Хорошее решение, но не хватает некоторых настроек. Как ограничить перетаскивание, чтобы не покидать экран? С текущим решением пользователь может полностью перетащить изображение из квадрата. - person Bugdr0id; 13.07.2021

zoomable устарел, можно использовать PointerInputScope.detectTransformGestures

@Composable
fun ImagePreview(link: String) {
    Box(modifier = Modifier.fillMaxSize()) {
        var angle by remember { mutableStateOf(0f) }
        var zoom by remember { mutableStateOf(1f) }
        var offsetX by remember { mutableStateOf(0f) }
        var offsetY by remember { mutableStateOf(0f) }

        CoilImage(
            data = link,
            contentDescription = "image",
            contentScale = ContentScale.Fit,
            modifier = Modifier
                .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                .graphicsLayer(
                    scaleX = zoom,
                    scaleY = zoom,
                    rotationZ = angle
                )
                .pointerInput(Unit) {
                    detectTransformGestures(
                        onGesture = { _, pan, gestureZoom, gestureRotate ->
                            angle += gestureRotate
                            zoom *= gestureZoom
                            val x = pan.x * zoom
                            val y = pan.y * zoom
                            val angleRad = angle * PI / 180.0
                            offsetX += (x * cos(angleRad) - y * sin(angleRad)).toFloat()
                            offsetY += (x * sin(angleRad) + y * cos(angleRad)).toFloat()
                        }
                    )
                }
                .fillMaxSize()
        )
    }
}
person Arun Padiyan    schedule 23.04.2021
comment
Он не уменьшает масштаб должным образом, в противном случае он работает, как ожидалось. - person Waqas Tahir; 11.07.2021
comment
при максимальном увеличении - person Waqas Tahir; 11.07.2021