구현해야 하는 기능
- 유저가 이미지를 선택하면 해당 이미지에서 어느 부분이 잘릴 예정인지 미리보기 제공
- 미리보기에서 유저는 확대, 축소, 이동을 통해 잘리는 영역을 조절할 수 있음
@Composable
fun ImagePreviewContent(
selectedUri: Uri? = null,
onClickBtnConfirm: (Float, Float, Float) -> Unit = { _, _, _ -> }
) {
var scale by remember { mutableFloatStateOf(1f) }
var offsetX by remember { mutableFloatStateOf(0f) }
var offsetY by remember { mutableFloatStateOf(0f) }
val state = rememberTransformableState { zoomChange, offsetChange, _ ->
scale *= zoomChange
offsetX += offsetChange.x
offsetY += offsetChange.y
}
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Image(
painter = rememberAsyncImagePainter(model = selectedUri),
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.graphicsLayer(
scaleX = maxOf(1f, scale),
scaleY = maxOf(1f, scale),
translationX = offsetX,
translationY = offsetY
)
.transformable(state = state),
contentScale = ContentScale.Crop
)
CropOverlay()
FilledBtn(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 80.dp)
.align(Alignment.BottomCenter),
text = WORD_CONFIRM,
onClickBtn = { onClickBtnConfirm(scale, offsetX, offsetY) }
)
}
}
@Composable
fun CropOverlay(
backgroundColor: Color = GsBlack.copy(alpha = 0.7f)
) {
Column(
modifier = Modifier
.fillMaxSize()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.background(backgroundColor)
)
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(375f / 235f)
.background(Color.Transparent)
.clip(RectangleShape)
)
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.background(backgroundColor)
)
}
}
위의 코드에는 생략되어 있지만 ImagePreviewContent가 이 화면의 최상위 컴포저블이 아니다. 더 상위의 컴포저블이 존재하기 때문에 scale, offsetX, offsetY 등의 상태 변수들을 상태 호이스팅을 적용하여 상위 컴포저블로 옮길지, 현재 컴포저블에 남길지는 고민해볼 여지가 있다.