🎨 UI – Views & Layouts · Lesson 15
Spinner, AutoComplete & Date/Time Pickers
Spinner
A Spinner shows the selected value and opens a list when tapped. Its items come from an adapter.
XML
<Spinner
android:id="@+id/spClass"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- res/values/arrays.xml -->
<string-array name="classes">
<item>Class 9</item>
<item>Class 10</item>
<item>Class 11</item>
<item>Class 12</item>
</string-array>Kotlin
val classes = resources.getStringArray(R.array.classes)
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, classes)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.spClass.adapter = adapter
binding.spClass.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
Log.d("Form", "Selected ${classes[position]}")
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
val selected = binding.spClass.selectedItem as StringMaterial exposed dropdown (recommended)
XML + Kotlin
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilSubject"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Subject">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/actvSubject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
// Kotlin
val subjects = listOf("Maths", "Physics", "Chemistry", "Biology", "English")
binding.actvSubject.setAdapter(ArrayAdapter(this, android.R.layout.simple_list_item_1, subjects))
binding.actvSubject.setText(subjects.first(), false) // false = do not filter
binding.actvSubject.setOnItemClickListener { _, _, position, _ ->
Toast.makeText(this, subjects[position], Toast.LENGTH_SHORT).show()
}AutoCompleteTextView suggestions
XML + Kotlin
<AutoCompleteTextView
android:id="@+id/actvCity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:hint="City" />
// Kotlin – suggestions appear after typing 1 letter
val cities = arrayOf("Agra", "Aligarh", "Allahabad", "Delhi", "Dehradun", "Lucknow")
binding.actvCity.setAdapter(ArrayAdapter(this, android.R.layout.simple_list_item_1, cities))MaterialDatePicker
Kotlin
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointForward
import com.google.android.material.datepicker.MaterialDatePicker
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone
binding.btnDate.setOnClickListener {
val constraints = CalendarConstraints.Builder()
.setValidator(DateValidatorPointForward.now()) // no past dates
.build()
val picker = MaterialDatePicker.Builder.datePicker()
.setTitleText("Exam date")
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.setCalendarConstraints(constraints)
.build()
picker.addOnPositiveButtonClickListener { utcMillis ->
val fmt = SimpleDateFormat("dd MMM yyyy", Locale.getDefault())
fmt.timeZone = TimeZone.getTimeZone("UTC") // the selection is UTC midnight
binding.btnDate.text = fmt.format(Date(utcMillis))
}
picker.show(supportFragmentManager, "date")
}MaterialTimePicker
Kotlin
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
binding.btnTime.setOnClickListener {
val picker = MaterialTimePicker.Builder()
.setTimeFormat(TimeFormat.CLOCK_12H)
.setHour(9)
.setMinute(30)
.setTitleText("Class time")
.build()
picker.addOnPositiveButtonClickListener {
val h = picker.hour
val m = picker.minute
val amPm = if (h < 12) "AM" else "PM"
val h12 = if (h % 12 == 0) 12 else h % 12
binding.btnTime.text = String.format(Locale.getDefault(), "%d:%02d %s", h12, m, amPm)
}
picker.show(supportFragmentManager, "time")
}Date + time together
Kotlin – Student Diary
// From Student Diary – AddEditNoteFragment.pickDateTime()
datePicker.addOnPositiveButtonClickListener { utcMillis ->
val utc = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply { timeInMillis = utcMillis }
val timePicker = MaterialTimePicker.Builder().setTimeFormat(TimeFormat.CLOCK_12H).setHour(9).build()
timePicker.addOnPositiveButtonClickListener {
val local = Calendar.getInstance().apply {
set(utc.get(Calendar.YEAR), utc.get(Calendar.MONTH), utc.get(Calendar.DAY_OF_MONTH),
timePicker.hour, timePicker.minute, 0)
}
dueAt = local.timeInMillis // used to schedule the reminder
}
timePicker.show(childFragmentManager, "time")
}Common mistakes
- Formatting the date picker result in local time → shows the previous day in some time zones. Treat it as UTC.
- Showing pickers with
childFragmentManagerin an Activity → usesupportFragmentManager(Fragments usechildFragmentManager). - Exposed dropdown with filtering on → after setText the list shows only one item; pass
falseas the second argument.
Summary
- Spinner and dropdowns use an ArrayAdapter; read the selection by position.
- Prefer the Material exposed dropdown inside TextInputLayout.
- MaterialDatePicker (UTC millis) + MaterialTimePicker (hour/minute) for date-time input.
✅ Practice
Make an "Exam form": class (exposed dropdown), city (AutoComplete), exam date (future dates only) and time. Show everything in one TextView after "Submit".
Frequently Asked Questions
Spinner or exposed dropdown menu?
Both show a list to pick from. The Material exposed dropdown matches TextInputLayout fields, supports a hint, errors and typing, so it looks better in modern forms.
Why does MaterialDatePicker give a time in UTC?
The selection is midnight UTC of the chosen day. Convert it using a UTC Calendar to get the year, month and day, then build a local Calendar.
How do I stop users choosing past dates?
Set CalendarConstraints with DateValidatorPointForward.now() on the date picker builder.
