카테고리 없음

Android 배워보자 Compose! -(6) Spinner 를 Compose 에서

wannagohome97 2024. 3. 18. 16:18

Spinner -> DropdownMenu!

Spinner 라는 레이아웃 요소는 기존 XML 에서 드롭다운 형태의 메뉴를 구현할 때 많이 사용되었는데

Compose 로 넘어오면서 "DropdownMenu" 라는 개념으로 바뀌어 들어왔습니다.

이전 ProgressBar 같은 요소들과는 또 다르게 구현 방식 자체가 좀 많이 바뀌었다고 느껴졌던 요소였습니다.

 

DropdwonMenu?

@Composable
fun DropdownMenu(
    expanded: Boolean,
    onDismissRequest: () -> Unit,
    modifier: Modifier = Modifier,
    offset: DpOffset = DpOffset(0.dp, 0.dp),
    scrollState: ScrollState = rememberScrollState(),
    properties: PopupProperties = PopupProperties(focusable = true),
    content: @Composable ColumnScope.() -> Unit
) {
    val expandedState = remember { MutableTransitionState(false) }
    expandedState.targetState = expanded

    if (expandedState.currentState || expandedState.targetState) {
        val transformOriginState = remember { mutableStateOf(TransformOrigin.Center) }
        val density = LocalDensity.current
        val popupPositionProvider = remember(offset, density) {
            DropdownMenuPositionProvider(
                offset,
                density
            ) { parentBounds, menuBounds ->
                transformOriginState.value = calculateTransformOrigin(parentBounds, menuBounds)
            }
        }

        Popup(
            onDismissRequest = onDismissRequest,
            popupPositionProvider = popupPositionProvider,
            properties = properties
        ) {
            DropdownMenuContent(
                expandedState = expandedState,
                transformOriginState = transformOriginState,
                scrollState = scrollState,
                modifier = modifier,
                content = content
            )
        }
    }
}

 

라이브러리 상 DropdownMenu 의 구현입니다.

Dialog 와 같이 Boolean State 를 통해 화면에 Composed 여부가 결정되고

디바이스의 Back 에 반응해서 Dismiss 됩니다.

 

그리고 ColumnScope 인 후행 람다 내부에 Item 들을 넣어주는 형태인데

 

기존에 ArrayList 를 인자로 받고 ViewType 을 별도로 구현하던 Spinner 와 비교하면 비슷한 듯 다릅니다.

 

내부 아이템 = DropdownMenuItem

@Composable
fun DropdownMenuItem(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit
) {
    DropdownMenuItemContent(
        onClick = onClick,
        modifier = modifier,
        enabled = enabled,
        contentPadding = contentPadding,
        interactionSource = interactionSource,
        content = content
    )
}

 

이걸 DropdownMenu 의 후행 람다로 오는 ColumnScope 에 배열해주는게 일반적입니다.

 

쉽게 설명하자면 Button 과 유사합니다.(후행 람다로 Rowscope 를 가지고 , onClick 을 Parameter 로 받습니다.)

 

var isExpanded by remember {
    mutableStateOf(false)
}
Button(
    modifier = Modifier.width(180.dp),
    onClick = { isExpanded = true }) {
    Text(text = "Click to show Menu")
}
DropdownMenu(
    modifier = Modifier.width(180.dp),
    expanded = isExpanded,
    onDismissRequest = { isExpanded = false }) {
    DropdownMenuItem(onClick = {
        Toast.makeText(context, "Clicked Item = 1", Toast.LENGTH_SHORT).show()
    }) {
        Text(text = "Menu Item 1")
    }
    DropdownMenuItem(onClick = {
        Toast.makeText(context, "Clicked Item = 2", Toast.LENGTH_SHORT).show()
    }) {
        Text(text = "Menu Item 2")
    }
}

 

대략 이런 식으로 구현해주면

 

이 버튼을 눌렀을때

 

이렇게 나옵니다(다른 글들 보면 움짤로 보여드렸었는데 이상하게 PC 렉이 심해서 추후 수정 예정..)