컴포저블의 포지션은 어떻게 구할 수 있나

Android View로 애플리케이션 UI를 구현할 때는 root 에서 하위에 있는 view의 포지션을 구할 수 있었다. 하지만 컴포즈로 UI를 구현시에는 View는 ComposeView 하나만 존재하고, 캔버스에 필요한 Element들을 그리는 방식이다. 

위의 이미지와 같이 특정 버튼(컴포저블) 위치에 맞춰 어떠한 팝업을 띄운다고 가정할 때 어떻게 포지션을 구할 수 있을까?

사실 컴포즈에서도 ConstraintLayout으로만 UI를 구성한다면, 특별한 솔루션이 필요 없을지도 모른다. 그렇지 않은 경우에는 컴포저블의 포지션을 알아야 한다.

OnGloballyPositionedModifier 사용하기

interface OnGloballyPositionedModifier : Modifier, Modifier.Element

OnGloballyPositionedModifier는 Modifier의 일종으로, 콘텐츠의 전역 포지션이 변경되었을 때 레이아웃의 최종 LayoutCoordinates와 함께 onGloballyPositioned 콜백을 호출한다. 좌표를 포함하고 있는 이 콜백은 Composition(구성)이 끝났을 때 호출 됨을 명심하자.

좌표 구하기 예제

Column(
    Modifier.onGloballyPositioned { coordinates ->
        // Column의 사이즈
        coordinates.size
        // 애플리케이션 윈도우에 상대적인 Column의 포지션
        coordinates.positionInWindow()
        // 컴포즈 최상위에 상대적인 Column의 포지션
        coordinates.positionInRoot()
        // 레이아웃에 제공되는 정렬 라인 (Column의 경우 비어있음)
        coordinates.providedAlignmentLines
        //  Column의 부모에 해당하는 LayoutCoordinates 인스턴스
        coordinates.parentLayoutCoordinates
    }
) {
    ...
}

버튼 위치에 맞게 팝업 띄우기

좌표를 구하고자 하는 컴포저블에 OnGloballyPositionedModifier를 추가하여 상대적인 포지션을 먼저 구할 수 있다.

@Composable
fun InformationButton(
    ...,
    onGloballyPositioned:(LayoutCoordinates) -> Unit,
) {
    Column(
        modifier = modifier.onGloballyPositioned(onGloballyPositioned)
    ) {
        ...
    }
}

state hoisting 기법으로 전역 포지션 정보(LayoutCoordinates) 이벤트를 상위로 전달한다.

@Composable
private fun RequestPaymentScreen(
    ...
) {
    ...

    var infomationBtnOffset by remember { mutableStateOf(Offset.Zero) }

    Scaffold(
        topBar = {...},
        content = {

            ...

            InformationButton(
                // boundsInRoot는 해당 컴포저블을 감싸는  정보를 반환한다.
                // Rect의 bottomCenter를 중앙 하단부 포지션(Offset)을 반환한다.
                // 이 정보를 informationBtnOffset에 저장하자.
                onGloballyPositioned = { layoutCoordinates->
                    layoutCoordinates.boundsInRoot().bottomCenter
                }
            )
            
        },
        bottomBar = {...}
    )
   
}

전달 받은 전역 포지션의 상태를 저장하고 State로 저장(remember)한다.



@Composable
private fun RequestPaymentScreen(
    ...
) {
    ...

    var infomationBtnOffset by remember { mutableStateOf(Offset.Zero) }

    Scaffold(...)

    // 화면위에 나타내고 싶은 팝업을 보여주고, offset 정보를 기반으로 
    PopupGuide(
        isShowing = ...,
        offset = infomationBtnOffset,
        onCloseClick = ...
    )
   
}

@Composable
fun PopupGuide(
    isShowing: Boolean,
    offset: Offset,
    onCloseClick: () -> Unit
) {
    val paddingStart = ... // offset 기반으로 시작 패딩값을 결정
    val paddingTop = ... // offset 기반으로 상단 패딩값을 결정

    if (isShowing) {
        Surface{
            Column(
                modifier = Modifier
                    .padding(
                        start = paddingStart,
                        top = paddingTop
                    )
            ) { ... }
        }
    }
}

컴포저블의 위치정보를 기반으로 팝업의 위치를 적절하게 배치할 수 있다.

카테고리: 미분류

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.