DropDownMenu란?
DropDownMenu는 버튼을 눌렀을 때 선택지를 보여주는 메뉴 아이템이다. 동작은 그림1과 같다.
Compose의 DropDownMenu
Compose의 Material package에서는 compose.ui.window 패키지의 Popup을 활용해서 DropDownMenu를 만드는 것을 지원한다. Popup을 활용함으로써 부모 Composable의 제약사항(크기 등)에 종속되지 않는 컴포저블을 만들 수 있다.
또한 Material package에서는 미리 디자인된 컴포저블인 DropDownMenuItem 또한 지원한다. DropDownMenuItem을 이용하면 DropDownMenu에 들어갈 아이템들을 쉽게 만들 수 있다.
우리는 이번 글에서 DropDownMenu와 DropDownMenuItem 두가지를 이용해 DropDownMenu를 만드는 방법을 다룬다.
DropDownMenu의 구성요소
DropDownMenu는 다음의 구성요소로 구성되어 있다.
- expanded : DropDownMenu가 펼쳐졌는지 여부
- onDismissRequest : DropDownMenu를 닫으라는 명령(Dismiss Request)이 떨어졌을 때의 동작
- offset : DropDownMenu를 호출하는 Composable의 기준점으로부터의 거리(offset) 설정
- properties : 백버튼을 눌렀을 때 DropDownMenu를 Dismiss 할 것인지, DropDownMenu의 바깥쪽을 눌렀을 때 Dismiss할 것인지 등의 DropDownMenu의 기본 동작을 정의.
- content : DropDownMenu안에 들어갈 메뉴 아이템을 넣는 공간
*modifier은 Composable의 공통 사항이므로 다루지 않는다.
@Composable
fun DropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset(0.dp, 0.dp),
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit
)
DropDownMenuItem의 구성요소
DropDownMenuItem은 다음의 구성요소로 구성되어 있다.
- onClick : 메뉴 아이템이 눌렸을 때의 동작 정의
- enabled : 메뉴 아이템을 클릭 가능하게 할 것인지 여부(눌렸을 때 onClick이 동작할 것인지 여부)
- contentPadding : DropDownMenuItem에 적용할 Padding값
- interactionSource : DropDownMenuItem과 사용자와의 Interaction에 대한 이벤트를 관리. 여기서는 클릭 이벤트만 다루므로 따로 다루지 않는다.
- content : DropDownMenuItem에 표기할 요소 관리 (텍스트 등)
fun DropdownMenuItem(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit
)
이번 글에서는 기본적인 구성요소들을 이용해 DropDownMenu를 만드는 방법을 다루고
다른 구성요소들에 관해서는 다음 글에서 심화하면서 다룬다.
DropDownMenu와 DropDownMenuItem을 이용해 DropDownMenu 만들기
DropDownMenu를 위해서는 3가지의 구성요소가 필요하다.
- DropDownMenu가 펼쳐졌는지를 제어할 수 있는 변수
- DropDownMenu의 펼쳐짐 상태를 제어하기 위한 버튼 혹은 onClick이벤트를 포함한 Composable
- DropDownMenu 정의
자 이제 만들어보도록 하자.
1. DropDownMenu가 펼쳐졌는지를 제어할 수 있는 Boolean 변수를 Composable 내에 만든다.
@Composable
fun KotlinWorldButtonWithDropDownMenu() {
// 1. DropDownMenu의 펼쳐짐 상태 정의
var isDropDownMenuExpanded by remember { mutableStateOf(false) }
}
isDropDownMenuExpanded 값은 Recomposition이 일어날 때 저장되어야 하고 state가 바뀔 때 Recomposition을 일어나도록 만들어야 한다. Recomposition 시 값을 저장하기 위해 remember을 쓰고, Composable 내에서 State를 인식할 수 있도록 Mutable State를 사용한다.
2. 1에서 만든 변수를 제어할 수 있는 버튼을 만든다. 클릭 시 변수값이 true로 바뀌어 DropDownMenu가 펼쳐지도록 한다.
@Composable
fun KotlinWorldButtonWithDropDownMenu() {
// 1. DropDownMenu의 펼쳐짐 상태 정의
var isDropDownMenuExpanded by remember { mutableStateOf(false) }
// 2. DropDownMenu의 Expanded 상태를 변경하기 위한 버튼 정의
Button(
onClick = { isDropDownMenuExpanded = true }
) {
Text(text = "Show Menu")
}
}
Button의 기능은 클릭이 일어났을 때 isDropDownMenuExpanded 변수를 true로 State를 바꾸게 해서 Recomposition이 일어나게 하는 것이다.
3. DropDownMenu 정의
@Composable
fun KotlinWorldButtonWithDropDownMenu() {
// 1. DropDownMenu의 펼쳐짐 상태 정의
var isDropDownMenuExpanded by remember { mutableStateOf(false) }
// 2. DropDownMenu의 Expanded 상태를 변경하기 위한 버튼 정의
Button(
onClick = { isDropDownMenuExpanded = true }
) {
Text(text = "Show Menu")
}
// 3. DropDownMenu 정의
DropdownMenu(
modifier = Modifier
.wrapContentSize(),
expanded = isDropDownMenuExpanded,
onDismissRequest = { isDropDownMenuExpanded = false }
) {
// 3.1. DropDownMenuItem을 정의하고 눌렸을 때 Hello가 출력되도록 함
DropdownMenuItem(onClick = {
println("Hello")
}) {
Text(text = "Print Hello")
}
// 3.2. DropDownMenuItem을 정의하고 눌렸을 때 KotlinWorld가 출력되도록 함
DropdownMenuItem(onClick = {
println("KotlinWorld")
}) {
Text(text = "Print KotlinWorld")
}
}
}
DropDownMenu의 expanded 파라미터 값으로 1에서 정의한 isDropDropMenuExpanded 변수를 넘기고 onDismissRequest가 발생했을 때 isDropDropMenuExpanded를 false로 변경하여 DropDownMenu가 사라지도록 만든다.
내부에는 DropDownMenuItem을 두 개 정의한다. 하나는 눌렸을 때 Hello가 출력되는 아이템이고, 다른 하나는 KotlinWorld가 출력되는 아이템이다.
4. 결과
이 세가지를 이용해 코드를 만들어 실행하면 다음과 같이 출력된다.