- Published on
- ·7 min read
Implementing Dropdown (Select Box) Using Spinner in Android
I apologize in advance for any awkward expressions in English.
English is not my native language, and I have relied on ChatGPT's assistance to proceed with the translation.
Overview
The smart desk clock in the sosone project (Android) includes a font selection feature. Fonts can be selected via a dropdown, and the selected item is highlighted with a different color.
Service Shortcut
Screen Preview
Fragment screens are used, and when a font is clicked, a Spinner (Dropdown) is displayed in the form of a dialog.
The Spinner (Dropdown) operation screen looks like the image below.
Brief Introduction to the Work
Layout Section
- Added
<Spinner ... />
to the layout - Added layouts for each item in the Spinner (res/layout/spinner_list_item.xml)
- Added a
drawable
to highlight the selected item (res/drawable/selector_spinner_background.xml)
Kotlin Code Section
- Connected an
Adapter
to the Spinner - (Optional) Defined the array for the Spinner using Enum (EnumFont.kt)
- Set the previously selected item as the default using
setSelection
- Set up operations when an item is selected with a
Listener
layout
fragment_setting.xml
- This is the layout for the settings screen with a font Spinner list.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...Omitted...>
<ScrollView
android:id="@+id/view_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Omitted -->
<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>
TextView for displaying font selection: We used
MaterialTextView
to maintain consistency with other material components used in the settings.Spinner configuration: You can choose between
android:spinnerMode="dialog"
orandroid:spinnerMode="dropdown"
.
spinner_list_item.xml
This is the layout for each item (font) in the Spinner.
It is connected to the Spinner's Adapter
(refer to SettingFragment.kt).
We use selector
in the android:background
to highlight the selected item (refer to 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
This section is used to highlight the selected item. It is displayed with the specified color when in the state_activated
state.
<?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
Please refer to the comments in the code.
class SettingFragment : Fragment() {
private var _binding : FragmentSettingBinding? = null
private val binding get() = _binding!!
// The configured data is managed in the view model.
private val viewModel: ClockViewModel by activityViewModels{
ClockViewModelFactory(
(activity?.application as ClockApplication).database.settingDao()
)
}
// ...omitted...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Fonts are defined as an enum since they have different names to display to the user and load in reality.
val fontItems = enumValues<EnumFont>()
// Specify the ArrayAdapter to load into the Spinner.
val arrayAdapter = ArrayAdapter(requireContext(), R.layout.spinner_list_item, fontItems)
binding.spinnerFontSelect.adapter = arrayAdapter
// If there is selected font information, set it with setSelection.
if(viewModel.font.value != null && viewModel.font.value!!.isNotEmpty()){
binding.spinnerFontSelect.setSelection(
arrayAdapter.getPosition(
EnumFont.valueOf(
viewModel.font.value!!
)
)
)
}
// Define a Listener for font selection.
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)
}
}
// Added functionality to open the font Spinner when the font TextView is clicked.
binding.fontHint.setOnClickListener { binding.spinnerFontSelect.performClick() }
}
}
EnumFont.kt
Font names are different for the user interface and the actual font loading, so they are defined using an 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"),
// omitted
}
Additional Information
Initially, I used the material AutoCompleteTextView
as follows:
<com.google.android.material.textfield.TextInputLayout ... omitted ...>
<AutoCompleteTextView ... omitted ... />
</com.google.android.material.textfield.TextInputLayout>
The reason was that it seemed more appealing than Spinner due to better design, adjustment of the Dropdown's starting position, and height control. However, I switched to Spinner because I couldn't resolve the following issues due to my limited technical knowledge:
- When a selection was set using
setText
, the previous Dropdown list disappeared upon rotation, leaving only the selected item. - It was cumbersome to specify a separate state to highlight the selected item.