- Published on
- ·6 min read
Spinner 이용하여 Dropdown(select box) 구현(안드로이드)
개요
sosone 프로젝트의 스마트 탁상 시계(안드로이드)에는 폰트를 선택하는 기능이 있습니다.
폰트는 Dropdown으로 선택할 수 있고, 선택된 항목은 색상을 달리해 강조되도록 했습니다.
서비스 바로가기
화면 미리보기
Fragment 화면을 이용했으며,
font 클릭 시 Dialog 형태로 Spinner(Dropdown)가 표시됩니다.
Spinner(Dropdown) 동작 화면은 아래 이미지와 같습니다.
작업 간략 소개
Layout 부분
- layout 에
<Spinner ... />
추가 - Spinner 각 항목에 대한 layout 추가 (res/layout/spinner_list_item.xml)
- 선택된 항목을 강조 표시하기 위한
drawable
추가 (res/drawable/selector_spinner_background.xml)
Kotlin 코드 부분
- Spinner 에
Adapter
연결- (선택) Spinner에 들어갈 배열은 Enum으로 정의함 (EnumFont.kt)
- 이전에 선택된 항목이 Default로 선택되도록
setSelection
- 항목 선택 시 작업
Listener
설정
layout
fragment_setting.xml
- font Spinner 목록이 있는 설정 화면 레이아웃입니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...생략...>
<ScrollView
android:id="@+id/view_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 생략 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/font_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="font"
/>
<Spinner
android:id="@+id/spinner_font_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
/>
</LinearLayout>
</ScrollView>
</LinearLayout>
font 선택용을 표시할 TextView- 다른 설정들을 material 컴포넌트를 사용했기 때문에 동일하게
MaterialTextView
를 사용했습니다.Spinner 설정-
android:spinnerMode
="dialog"
또는"dropdown"
을 선택할 수 있습니다.
spinner_list_item.xml
Spinner의 각 항목(폰트)에 대한 layout입니다.
Spinner의 Adapter
에 연결됩니다. (SettingFragment.kt 참고)
android:background
로 selector
를 사용하여 선택된 항목을 강조 표시할 수 있도록 했습니다.
(selector_spinner_background.xml 부분 참고)
<?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:padding="8dp"
android:ellipsize="marquee"
android:maxLines="1"
android:textAppearance="?attr/textAppearanceSubtitle1"
android:background="@drawable/selector_spinner_background"
/>
selector_spinner_background.xml
선택된 항목을 강조 표시하기 위한 부분입니다.
state_activated
상태일 때 해당 색상으로 표시됩니다.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/teal_700" android:state_activated="true" />
</selector>
Kotlin Source (Fragment)
SettingFragment.kt
코드 내 주석을 참고해 주세요.
class SettingFragment : Fragment() {
private var _binding : FragmentSettingBinding? = null
private val binding get() = _binding!!
// 설정한 Data는 viewMode에서 관리
private val viewModel: ClockViewModel by activityViewModels{
ClockViewModelFactory(
(activity?.application as ClockApplication).database.settingDao()
)
}
// ...생략...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// font는 사용자에게 보여는 것과, 실제 로드해야하는 이름이 달라 enum으로 정의
val fontItems = enumValues<EnumFont>()
// Spinner에 로드할 ArrayAdapter 지정
val arrayAdapter = ArrayAdapter(requireContext(), R.layout.spinner_list_item, fontItems)
binding.spinnerFontSelect.adapter = arrayAdapter
// 선택된 폰트 정보가 있을 경우 setSelection으로 지정
if(viewModel.font.value != null && viewModel.font.value!!.isNotEmpty()){
binding.spinnerFontSelect.setSelection(
arrayAdapter.getPosition(
EnumFont.valueOf(
viewModel.font.value!!
)
)
)
}
// 폰트 선택 시 Listener 정의
binding.spinnerFontSelect.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onNothingSelected(parent: AdapterView<*>?) {
println("onNothingSelected")
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
println("onItemSelected")
val selectedFont = parent?.getItemAtPosition(position) as EnumFont
viewModel.changeFont(selectedFont.name)
}
}
// font TextView 클릭시에 font Spinner 열리도록 추가
binding.fontHint.setOnClickListener { binding.spinnerFontSelect.performClick() }
}
}
EnumFont.kt
font는 사용자에게 보여는 것과, 실제 로드해야 하는 이름이 달라 enum으로 정의했습니다.
enum class EnumFont(val fontName: String) {
DEFAULT("default"),
BLACK_HAN_SANS("black_han_sans"),
CUTE_FONT("cute_font"),
DONGLE("dongle_regular"),
DONGLE_BOLD("dongle_bold"),
GEO("geo"),
GUGI("gugi"),
MONTSERRAT("montserrat_bold"),
NANUM_GOTHIC("nanum_gothic_bold"),
// 생략
}
덧붙이는 내용
처음에는 아래와 같이 material AutoCompleteTextView
를 이용했습니다.
<com.google.android.material.textfield.TextInputLayout ... 생략 ...>
<AutoCompleteTextView ... 생략 ... />
</com.google.android.material.textfield.TextInputLayout>
이유는 Spinner 보다 디자인, Dropdown 시작 위치 조정, 높이 조정 등 더 좋아 보였습니다.
하지만 Spinner로 변경한 이유는 저의 기술 부족 문제로 아래의 문제들을 해결할 수 없었기 때문입니다.
- setText로 선택된 항목이 표시된 상태에서, 회전이 되면 기존 Dropdown 목록이 없어지고 선택된 항목만 남는 버그가 발견됐습니다.
- 선택된 항목을 강조 표시하기 위해 따로 상태값을 지정해줘야 하는 번거로움이 있었습니다.