Buttons, CheckBox & RadioButton

Starting with this lesson, we build one running example across the rest of this track: RecipeBox, a recipe-manager app. Its core data class looks like this, and we will reuse it in every upcoming lesson:

// Recipe.java
public class Recipe {
    public String id;
    public String title;
    public String ingredients;
    public String steps;
    public String photoUri;
    public String category;
    public int prepMinutes;
    public boolean isFavorite;
    public String difficulty; // "Easy", "Medium", "Hard"
}

Button, CheckBox and RadioButton are the three most common widgets for letting a user act on or choose between options. Every one of them is a subclass of TextView under the hood, so anything you already know about android:text or styling text applies here too.

Note: this site does not run a live Android Studio build. Every snippet below is a complete, correct, static example you can drop straight into a real project — walk through the explanation under each block to see exactly what it does.

Button: Triggering an Action

A Button (or its Material replacement, com.google.android.material.button.MaterialButton) fires a single event when tapped. In RecipeBox, the "add/edit recipe" screen ends with a Save button:

<Button
    android:id="@+id/btnSaveRecipe"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Save Recipe" />

The classic way to react to a tap is an anonymous View.OnClickListener:

Button btnSaveRecipe = findViewById(R.id.btnSaveRecipe);

btnSaveRecipe.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Recipe recipe = buildRecipeFromForm();
        saveRecipeToDatabase(recipe);
        Toast.makeText(RecipeFormActivity.this, "Recipe saved", Toast.LENGTH_SHORT).show();
    }
});

Since Java 8, most Android projects target a level that supports lambdas, and View.OnClickListener is a functional interface (a single abstract method, onClick), so it can be written far more compactly:

btnSaveRecipe.setOnClickListener(v -> {
    Recipe recipe = buildRecipeFromForm();
    saveRecipeToDatabase(recipe);
    Toast.makeText(this, "Recipe saved", Toast.LENGTH_SHORT).show();
});
Explanation:
  • Both forms register the same callback; the lambda is just less boilerplate for a single method.
  • Inside the anonymous class, this refers to the listener, so the enclosing Activity is referenced as RecipeFormActivity.this. Inside a lambda there is no new this, so plain this already means the Activity.
  • Prefer lambdas for new code in this current, modern style of Android development — they read cleaner and are what you'll see in most real-world Java Android codebases today.

CheckBox: Independent On/Off Options

A CheckBox represents a choice that is independent of any other choice on the screen — turning it on or off doesn't affect anything else. RecipeBox uses one on the recipe-detail screen to let the user mark a recipe as a favorite:

<CheckBox
    android:id="@+id/cbFavorite"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Mark as Favorite"
    android:checked="false" />
CheckBox cbFavorite = findViewById(R.id.cbFavorite);
cbFavorite.setChecked(recipe.isFavorite);

cbFavorite.setOnCheckedChangeListener((buttonView, isChecked) -> {
    recipe.isFavorite = isChecked;
    favoriteRepository.update(recipe);
});
Explanation: android:checked sets the initial state in XML; setChecked() sets it from Java (for example, once you've loaded the recipe from the database). OnCheckedChangeListener fires every time the box is toggled, from either a user tap or a programmatic setChecked() call.

RadioButton & RadioGroup: Mutually Exclusive Choices

When only one of several options can be selected at a time, use RadioButton widgets wrapped in a RadioGroup. RecipeBox uses this for picking a recipe's difficulty level:

<RadioGroup
    android:id="@+id/rgDifficulty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <RadioButton
        android:id="@+id/rbEasy"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Easy"
        android:checked="true" />

    <RadioButton
        android:id="@+id/rbMedium"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Medium" />

    <RadioButton
        android:id="@+id/rbHard"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hard" />

</RadioGroup>

Reading which one is checked, once the user taps Save:

RadioGroup rgDifficulty = findViewById(R.id.rgDifficulty);

btnSaveRecipe.setOnClickListener(v -> {
    int checkedId = rgDifficulty.getCheckedRadioButtonId();
    RadioButton checkedButton = findViewById(checkedId);
    recipe.difficulty = checkedButton.getText().toString();
    saveRecipeToDatabase(recipe);
});
Explanation:
  • Only one RadioButton inside the same RadioGroup can be checked; tapping a different one automatically unchecks the previous choice.
  • getCheckedRadioButtonId() returns the id of the selected button, or -1 if none is checked.
  • Setting android:checked="true" on one RadioButton in the XML gives you a sensible default ("Easy") the first time the form opens.

android:onClick — An Older Alternative

Older Android code sometimes wires a click straight from XML using android:onClick="methodName", which calls a public, no-argument, void method with that name on the hosting Activity:

<Button
    android:id="@+id/btnSaveRecipe"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Save Recipe"
    android:onClick="onSaveRecipeClicked" />
public void onSaveRecipeClicked(View view) {
    saveRecipeToDatabase(buildRecipeFromForm());
}
Why we don't recommend this: it's harder to trace (the wiring lives only in the layout file), harder to unit test, and doesn't work with lambdas or anonymous listeners. Stick with setOnClickListener() in Java for new code — it's mentioned here only because you will still see it in older codebases and tutorials.

Styling With Material Components

All three widgets have Material Design counterparts — MaterialButton, MaterialCheckBox and MaterialRadioButton — that apply consistent theming, ripple effects, and elevation automatically. The Java APIs above (click listeners, checked-state listeners, RadioGroup) work identically whether you use the plain widgets or their Material versions. See Material Components (Java) for how to switch a layout over and pick up Material styling with minimal XML changes.

Next up: displaying a recipe's photo with ImageView and turning an image into a tappable control with ImageButton → ImageView & ImageButton.