안드로이드/머티리얼 디자인

[안드로이드] 머티리얼 디자인 - Menu(DropDown)

안드뽀개기 2022. 4. 10. 12:15
반응형

 

 

이번엔 DropDown에 대해 알아보겠습니다~

이번 예제에서는 두 종류의 DropDown을 사용해볼 예정입니다.

 

 

 

프로젝트 전체 layout 입니다.

<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".skill.MaterialDesign3Activity">
<com.google.android.material.navigationrail.NavigationRailView
android:id="@+id/navigation_rail"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="@color/white"
app:itemIconTint="@color/select_navi_rail_icon_color"
app:itemTextColor="@color/select_navi_rail_text_color"
app:itemRippleColor="@color/bg_gray"
app:menu="@menu/menu_navigation_rail"
app:headerLayout="@layout/layout_rail_header"
app:labelVisibilityMode="unlabeled"
app:menuGravity="bottom"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnDropDown"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="DropDown 메뉴"
android:layout_marginTop="24dp"
app:layout_constraintWidth_percent="0.5"
app:layout_constraintStart_toEndOf="@+id/navigation_rail"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Hint"
app:helperText="HelperText"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@+id/btnDropDown"
app:layout_constraintStart_toStartOf="@+id/btnDropDown"
app:layout_constraintEnd_toEndOf="@+id/btnDropDown">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

 

 

 

먼저, res > menu 디렉토리에 xml 파일을 생성하고, 그 menu xml을 inflate하는 방식입니다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/dropDown1"
android:title="item 1"
android:icon="@drawable/ic_outline_one"/>
<item
android:id="@+id/dropDown2"
android:title="item 2"
android:icon="@drawable/ic_outline_two"/>
<item
android:id="@+id/dropDown3"
android:title="item 3"
android:icon="@drawable/ic_outline_three"/>
<item
android:id="@+id/dropDown4"
android:title="item 4"
android:icon="@drawable/ic_outline_four"/>
</menu>

 

 

 

 

그리고 다음 코드를 작성합니다.

@SuppressLint("RestrictedApi")
private fun showDropDownMenu(v: View){
val popup = PopupMenu(this, v)
popup.menuInflater.inflate(R.menu.menu_drop_down, popup.menu)
if (popup.menu is MenuBuilder) {
val menuBuilder = popup.menu as MenuBuilder
menuBuilder.setOptionalIconsVisible(true)
menuBuilder.visibleItems.forEach { item ->
val iconMarginPx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, resources.displayMetrics).toInt()
item.icon?.let {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
item.icon = InsetDrawable(it, iconMarginPx, 0, iconMarginPx, 0)
} else {
item.icon = object: InsetDrawable(it, iconMarginPx, 0, iconMarginPx, 0) {
override fun getIntrinsicWidth(): Int {
return it.intrinsicWidth + iconMarginPx + iconMarginPx
}
}
}
}
}
}
popup.show()
popup.setOnMenuItemClickListener {
when(it.itemId) {
R.id.dropDown1 -> {
Toast.makeText(this, "DropDown1 Clicked",Toast.LENGTH_SHORT).show()
}
R.id.dropDown2 -> {
Toast.makeText(this, "DropDown2 Clicked",Toast.LENGTH_SHORT).show()
}
R.id.dropDown3 -> {
Toast.makeText(this, "DropDown3 Clicked",Toast.LENGTH_SHORT).show()
}
R.id.dropDown4 -> {
Toast.makeText(this, "DropDown4 Clicked",Toast.LENGTH_SHORT).show()
}
}
false
}
popup.setOnDismissListener {
Snackbar.make(binding!!.btnDropDown, "DropDown Dismissed!!", Snackbar.LENGTH_SHORT).show()
}
}

 

showDropDownMenu(v: View)의 파라미터 v는 해당 드랍다운 메뉴가 발생하는 뷰의 입니다. 이 예제에서는 버튼 클릭 시 드랍다운 메뉴가 버튼 아래로 나타나게 됩니다. menu를 inflate한 후에 아이콘의 margin을 설정해주는 코드를 작성했습니다.(공식 홈페이지 참고했습니다.)

 

 

각 메뉴마다 이벤트를 처리할 수 있습니다.

popup.setOnMenuItemClickListener {
when(it.itemId) {
R.id.dropDown1 -> {
Toast.makeText(this, "DropDown1 Clicked",Toast.LENGTH_SHORT).show()
}
R.id.dropDown2 -> {
Toast.makeText(this, "DropDown2 Clicked",Toast.LENGTH_SHORT).show()
}
R.id.dropDown3 -> {
Toast.makeText(this, "DropDown3 Clicked",Toast.LENGTH_SHORT).show()
}
R.id.dropDown4 -> {
Toast.makeText(this, "DropDown4 Clicked",Toast.LENGTH_SHORT).show()
}
}
false
}

 

드랍다운 메뉴가 dismiss 됐을때의 이벤트를 처리할 수 있습니다.

popup.setOnDismissListener {
Snackbar.make(binding!!.btnDropDown, "DropDown Dismissed!!", Snackbar.LENGTH_SHORT).show()
}

 

버튼 이벤트에서 (btnDropDown) 함수를 실행합니다.

override fun onClick(v: View?) {
when(v?.id) {
R.id.btnReturn -> onBackPressed()
R.id.layoutDraw -> Toast.makeText(this, "Draw Clicked", Toast.LENGTH_SHORT).show()
R.id.btnDraw -> Toast.makeText(this, "Draw Clicked", Toast.LENGTH_SHORT).show()
R.id.btnDropDown -> showDropDownMenu(v)
}
}

 

여기까지 첫번째 DropDown을 만들어 봤습니다. 

 

 


 

 

다음은 ExposedDropDownMenu를 만들어 보겠습니다.

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Hint"
app:helperText="HelperText"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@+id/btnDropDown"
app:layout_constraintStart_toStartOf="@+id/btnDropDown"
app:layout_constraintEnd_toEndOf="@+id/btnDropDown">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>

 

private fun showExposedDropDownMenu() {
val items = listOf("items 1","items 2","items 3","items 4")
val adapter = ArrayAdapter(this, R.layout.list_item, items)
(binding!!.textInputLayout.editText as? AutoCompleteTextView)?.setAdapter(adapter)
(binding!!.textInputLayout.editText as? AutoCompleteTextView)?.setOnItemClickListener { parent, view, position, id ->
when(position) {
0 -> Toast.makeText(this, "ExposedDropDown 1 Clicked",Toast.LENGTH_SHORT).show()
1 -> Toast.makeText(this, "ExposedDropDown 2 Clicked",Toast.LENGTH_SHORT).show()
2 -> Toast.makeText(this, "ExposedDropDown 3 Clicked",Toast.LENGTH_SHORT).show()
3 -> Toast.makeText(this, "ExposedDropDown 4 Clicked",Toast.LENGTH_SHORT).show()
}
}
(binding!!.textInputLayout.editText as? AutoCompleteTextView)?.setOnDismissListener {
Snackbar.make(binding!!.btnDropDown, "ExposedDropDownMenu Dismissed!!", Snackbar.LENGTH_SHORT).show()
}
}

 

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:padding="10dp"
android:textSize="16dp"
android:textAppearance="?attr/textAppearanceBodyLarge" />
view raw list_item.xml hosted with ❤ by GitHub

위와 같이 list를 생성하고 ArrayAdapter를 생성한 후에 textInputLayout안에 AutoCompleteTextView의 adapter를 설정합니다. 그리고 showExposedDropDownMenu() 함수를 필요한 시기에 호출합니다. 저의 경우는 onCreate에서  호출했습니다.

 

각 아이템의 클릭 이벤트를 처리할 수 있습니다.

(binding!!.textInputLayout.editText as? AutoCompleteTextView)?.setOnItemClickListener { parent, view, position, id ->
when(position) {
0 -> Toast.makeText(this, "ExposedDropDown 1 Clicked",Toast.LENGTH_SHORT).show()
1 -> Toast.makeText(this, "ExposedDropDown 2 Clicked",Toast.LENGTH_SHORT).show()
2 -> Toast.makeText(this, "ExposedDropDown 3 Clicked",Toast.LENGTH_SHORT).show()
3 -> Toast.makeText(this, "ExposedDropDown 4 Clicked",Toast.LENGTH_SHORT).show()
}
}

 

ExposedDropDown 메뉴가 dismiss 됐을때 이벤를 처리 할 수 있습니다.

(binding!!.textInputLayout.editText as? AutoCompleteTextView)?.setOnDismissListener {
Snackbar.make(binding!!.btnDropDown, "ExposedDropDownMenu Dismissed!!", Snackbar.LENGTH_SHORT).show()
}

 

 

TextInputLayout의 style이 현재는 OutlineBox 스타일로 되어있는데, 다른 스타일의 형태도 있으니 참고하시면 되겠습니다.

Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu
Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu
Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu
Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu
view raw .kt hosted with ❤ by GitHub

 

반응형