Quick take: A Button in Android Kotlin is wired up with setOnClickListener and a lambda. View binding gives you a typed reference to the button without findViewById. Three common patterns: lambda for one-off handlers, function reference for reusable handlers, and OnClickListener interface for legacy Java interop. Always pair the click with feedback, a Toast is the simplest option, with event handling shown.
A Button in Android does what you'd expect: it's a View subclass that responds to taps. In Kotlin, the most common way to react to taps is setOnClickListener { ... } with a lambda. That single line replaces the four-line anonymous-inner-class boilerplate Java required, and it's what every modern tutorial uses.
But there are three legitimate ways to attach a click handler, and the choice matters when the same logic needs to run on multiple buttons or when you're reading old Java code. We'll walk through all three, plus the view binding setup that makes everything cleaner.
How Do You Add a Button in XML?
Start with the layout. Open res/layout/activity_main.xml and add a Button inside whatever container you're using. A LinearLayout works for a single button, a ConstraintLayout for anything more.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@+id/click_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me" />
</LinearLayout>
Three attributes are doing real work here: android:id gives the button a unique reference so Kotlin code can find it, android:text sets the visible label, and the layout_width/height pair tells the parent how big to make it. Use wrap_content for "exactly as big as the text" and match_parent for "fill the available space".
The button inherits a Material You style by default if your theme is Material 3 (the default for new projects in 2026). You'll get rounded corners and the system's accent colour without doing anything.
How Do You Set Up View Binding?
Skip findViewById. It's been the standard advice since 2019 and there's no good reason to use it in new code. Enable view binding in your module-level app/build.gradle.kts:
android {
// ... other config
buildFeatures {
viewBinding = true
}
}
Rebuild the project. Android Studio now generates a binding class per layout. For activity_main.xml, that's ActivityMainBinding. The class has a typed field for every view that has an android:id, so binding.clickButton returns Button directly.
In your activity:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.myapp.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
That ActivityMainBinding.inflate(layoutInflater) parses the XML once at creation time, gives you a binding object with all views ready, and you pass binding.root to setContentView. From here, binding.clickButton is a regular Kotlin reference, no casts, no nulls.
What's the Lambda Approach to OnClick?
The default, idiomatic pattern. Inside onCreate, after view binding is set up:
import android.widget.Toast
binding.clickButton.setOnClickListener {
Toast.makeText(this, "Button clicked", Toast.LENGTH_SHORT).show()
}
That's it. The lambda gets the clicked view as an implicit it parameter if you need it, though most click handlers don't. Toast.makeText builds the toast, .show() actually displays it. Forgetting .show() is a classic mistake, your code compiles and runs but nothing appears.
A more complete handler that does something useful:
private var clickCount = 0
binding.clickButton.setOnClickListener {
clickCount++
binding.clickButton.text = "Clicked $clickCount times"
Toast.makeText(this, "Tap #$clickCount", Toast.LENGTH_SHORT).show()
}
State updates, UI updates, and feedback in five lines. The same thing in Java circa 2014 was a fifteen-line anonymous inner class and a final-keyword dance to capture the counter.
When Should You Use a Function Reference?
When the same handler needs to run from multiple buttons, or when the handler is complex enough that an inline lambda hurts readability. Define a member function, then pass ::functionName instead of a lambda:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.clickButton.setOnClickListener(::handleClick)
binding.otherButton.setOnClickListener(::handleClick)
}
private fun handleClick(view: View) {
val label = when (view.id) {
R.id.click_button -> "Main button"
R.id.other_button -> "Other button"
else -> "Unknown"
}
Toast.makeText(this, "$label tapped", Toast.LENGTH_SHORT).show()
}
The ::handleClick syntax is a callable reference. Kotlin checks that handleClick's signature matches what setOnClickListener expects (a function taking View, returning Unit), and the rest is automatic.
When does this beat the lambda? In my experience, the threshold is about 5-6 lines of logic or when 3+ buttons need the same handler. Below that, the inline lambda reads better.
When Would You Use the OnClickListener Interface?
Mostly when reading or modifying older Java code, or when a library expects the raw interface. The full form looks like this:
binding.clickButton.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
Toast.makeText(this@MainActivity, "Clicked", Toast.LENGTH_SHORT).show()
}
})
That's a Kotlin object expression implementing the View.OnClickListener interface. Functionally identical to the lambda version, just verbose. Note the this@MainActivity qualified-this, because plain this inside the object expression would refer to the anonymous OnClickListener instance, not the activity. That's a footgun the lambda form quietly sidesteps.
You'd never write this in new code. It's worth knowing only because every Stack Overflow answer from before 2018 uses this pattern, and Android Studio's auto-conversion from Java sometimes lands you here.
How Do You Give Visual Feedback?
Toast is the quickest option, and we've used it above. The full signature:
Toast.makeText(context, message, duration).show()
contextis usuallythisinside an activitymessageis aStringor a string resource ID likeR.string.click_msgdurationisToast.LENGTH_SHORT(2 seconds) orToast.LENGTH_LONG(3.5 seconds)
A few things Toast can't do: custom durations, rich layouts, action buttons, position control beyond a couple of options. For any of those, switch to Snackbar from the Material Components library. Snackbar appears at the bottom of the screen, supports an action button ("Undo", "Retry"), and dismisses on swipe.
Quick example:
import com.google.android.material.snackbar.Snackbar
binding.clickButton.setOnClickListener {
Snackbar.make(binding.root, "Item saved", Snackbar.LENGTH_LONG)
.setAction("Undo") { undoSave() }
.show()
}
The Snackbar attaches to a coordinator-layout-aware view (here, binding.root), shows the message, and pops the Undo button on the right. For real apps, Snackbar is what you want 90% of the time. Toast is best for quick debug-style feedback during development.
What Are the Common Mistakes?
Three patterns I've seen burn new Android devs more than anything else:
- Forgetting
.show()on Toast. The code compiles, runs, and silently does nothing. - Using
findViewByIdand casting. Old habit from Java days. Switch to view binding, it's 30 seconds of build config and saves you fromClassCastExceptionat runtime. - Setting click listeners before
setContentView. The view doesn't exist yet, sobinding.clickButtonthrows. Always set the listener aftersetContentView(binding.root).
Once those are out of muscle memory, button handling becomes the easiest thing in Android. The hard parts come later, lifecycle, configuration changes, process death, but that's a different post.
Related
- Android Kotlin Introduction - the language basics, val/var, null-safety, lambdas
- Android Introduction - OS architecture, SDK/NDK, project layout context
- EditText with Toast in Android Kotlin - read user input on tap, validate, and show feedback
- ImageView in Android Kotlin - the other essential widget, displaying images