🎨 UI – Views & Layouts · Lesson 14
CheckBox, RadioButton, Switch & Chips
Which control for which choice?
| Control | Choice type | Example |
|---|---|---|
| CheckBox | Yes/No, or many options | Accept terms, select subjects |
| RadioButton (in RadioGroup) | Exactly one of a few | Morning / Evening batch |
| MaterialSwitch | On/Off that applies now | Dark mode |
| Chip (ChipGroup) | Compact single or multi choice, tags | Topics, filters, priority |
| Slider | A number in a range | Study hours 1–6 |
CheckBox
XML + Kotlin
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/cbTerms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I accept the terms" />
// Kotlin
binding.cbTerms.setOnCheckedChangeListener { _, isChecked ->
binding.btnRegister.isEnabled = isChecked
}
val accepted = binding.cbTerms.isCheckedRadioGroup and RadioButton
XML + Kotlin
<RadioGroup
android:id="@+id/rgBatch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rbMorning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="Morning" />
<RadioButton
android:id="@+id/rbEvening"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Evening" />
<RadioButton
android:id="@+id/rbWeekend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Weekend" />
</RadioGroup>
// Kotlin
val batch = when (binding.rgBatch.checkedRadioButtonId) {
R.id.rbMorning -> "Morning"
R.id.rbEvening -> "Evening"
R.id.rbWeekend -> "Weekend"
else -> "Not selected"
}
binding.rgBatch.setOnCheckedChangeListener { _, checkedId ->
Log.d("Form", "selected id = $checkedId")
}MaterialSwitch
XML + Kotlin
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchNotify"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Class reminders" />
// Kotlin
binding.switchNotify.setOnCheckedChangeListener { _, on ->
Toast.makeText(this, if (on) "Reminders ON" else "Reminders OFF", Toast.LENGTH_SHORT).show()
}ChipGroup
XML + Kotlin
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroupTopics"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.chip.Chip
android:id="@+id/chipPython"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Python" />
<com.google.android.material.chip.Chip
android:id="@+id/chipAndroid"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android" />
<com.google.android.material.chip.Chip
android:id="@+id/chipWeb"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Web" />
</com.google.android.material.chip.ChipGroup>
// Kotlin – all selected chips (multi-select)
val topics = binding.chipGroupTopics.checkedChipIds.map { id ->
binding.chipGroupTopics.findViewById<Chip>(id).text.toString()
}
// add a chip from code
val chip = Chip(this).apply { text = "MySQL"; isCheckable = true }
binding.chipGroupTopics.addView(chip)Multi select (default)
- Python ✓ Android ✓ Web
- checkedChipIds → list
- filters, tags
Single select
- app:singleSelection="true"
- checkedChipId → one id
- priority: Low / Medium / High
Slider
XML + Kotlin
<com.google.android.material.slider.Slider
android:id="@+id/sliderHours"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stepSize="1"
android:value="2"
android:valueFrom="1"
android:valueTo="6" />
// Kotlin
binding.sliderHours.addOnChangeListener { _, value, _ ->
binding.tvHours.text = "Study ${value.toInt()} hours daily"
}Example: preferences form
Kotlin – read everything on Save
binding.btnSave.setOnClickListener {
val batch = when (binding.rgBatch.checkedRadioButtonId) {
R.id.rbEvening -> "Evening"
R.id.rbWeekend -> "Weekend"
else -> "Morning"
}
val topics = binding.chipGroupTopics.checkedChipIds
.map { binding.chipGroupTopics.findViewById<Chip>(it).text }
.joinToString()
val hours = binding.sliderHours.value.toInt()
val reminders = binding.switchNotify.isChecked
if (topics.isEmpty()) {
Snackbar.make(binding.root, "Choose at least one topic", Snackbar.LENGTH_SHORT).show()
return@setOnClickListener
}
binding.tvSummary.text = "Batch: $batch\nTopics: $topics\nHours: $hours\nReminders: $reminders"
}Common mistakes
- RadioButtons not inside a RadioGroup → several can be checked at once.
- Setting
isCheckedin code triggers the listener → set the value first, then attach the listener (or remove it temporarily) — important in RecyclerView rows. - Chips without
Widget.Material3.Chip.Filterstyle → they cannot be checked.
Summary
- Read state with
isChecked,checkedRadioButtonId,checkedChipIds,slider.value. - React instantly with
setOnCheckedChangeListener/addOnChangeListener. - Choose the control by the type of choice.
✅ Practice
Build a "Pizza order" screen: size (RadioGroup), toppings (multi-select chips), extra cheese (CheckBox), quantity (Slider 1–5) and show the total price live as the user changes options.
Frequently Asked Questions
When should I use a Switch instead of a CheckBox?
Use a Switch for settings that take effect immediately (dark mode, notifications). Use a CheckBox when the user selects options that are saved later with a button.
How do I make only one RadioButton selectable?
Put them inside the same RadioGroup. The group automatically unchecks the others.
Can ChipGroup allow only one selection?
Yes. Set app:singleSelection="true", and app:selectionRequired="true" if one chip must always stay selected.
