CoordinatorLayout is a powerful ViewGroup introduced by the Android Support Library, now part of AndroidX, designed to orchestrate interactions between its child views. It allows you to create advanced UI patterns such as collapsing toolbars, floating action buttons that react to scrolling, and more complex animations. While Jetpack Compose is gaining traction, many Android projects still utilize XML layouts. Understanding CoordinatorLayout is essential for creating modern, interactive UIs in these XML-based projects.
What is CoordinatorLayout?
CoordinatorLayout is a layout that enhances Android’s UI by managing child views’ interactions. It watches for specific actions or events, such as scrolling, and communicates these events to child views through Behavior classes. Behaviors define how a child view should react to these events, allowing for intricate UI dynamics.
Why Use CoordinatorLayout?
- Advanced UI Interactions: Simplifies the creation of complex UI patterns like collapsing toolbars, snackbars, and more.
- Simplified Animations: Makes it easier to animate views in response to scrolling or other UI events.
- Compatibility: Part of AndroidX, ensuring compatibility with a wide range of Android versions.
- Centralized Coordination: Centralizes the management of view interactions, promoting cleaner and more maintainable code.
How to Implement CoordinatorLayout in Kotlin with XML
To implement CoordinatorLayout, you’ll primarily work with XML layouts and Kotlin code to define Behaviors.
Step 1: Add Dependencies
Ensure you have the AndroidX AppCompat and Material Design dependencies in your build.gradle file:
dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
}
Step 2: Define the CoordinatorLayout in XML
Create your main layout file (e.g., activity_main.xml) using CoordinatorLayout as the root element. Include child views like AppBarLayout, Toolbar, and RecyclerView.
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="256dp"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:focusable="true"
android:src="@android:drawable/ic_dialog_email"
app:layout_anchor="@id/appBar"
app:layout_anchorGravity="bottom|right|end" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Step 3: Set Up AppBarLayout and CollapsingToolbarLayout
Use AppBarLayout to create an app bar and CollapsingToolbarLayout for a toolbar that collapses as the user scrolls.
app:layout_scrollFlags="scroll|exitUntilCollapsed": Specifies how the toolbar should behave when the RecyclerView is scrolled.app:layout_collapseMode="pin": Pins the toolbar to the top of the screen when it’s collapsed.
Step 4: Configure RecyclerView with Scrolling Behavior
Associate RecyclerView with the appbar_scrolling_view_behavior to enable scrolling under the AppBarLayout. Add the following to your strings.xml:
<resources>
<string name="appbar_scrolling_view_behavior" translatable="false">com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior</string>
</resources>
Then, in your RecyclerView:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
Step 5: Add FloatingActionButton with Anchoring
Position FloatingActionButton (FAB) using anchor properties, ensuring it is tied to the AppBarLayout:
app:layout_anchor="@id/appBar": Anchors the FAB to the AppBarLayout.app:layout_anchorGravity="bottom|right|end": Positions the FAB at the bottom-right corner of the anchor.
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:focusable="true"
android:src="@android:drawable/ic_dialog_email"
app:layout_anchor="@id/appBar"
app:layout_anchorGravity="bottom|right|end" />
Step 6: Create RecyclerView Adapter and LayoutManager in Kotlin
Set up RecyclerView in your Kotlin Activity or Fragment:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.appcompat.widget.Toolbar
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val collapsingToolbar: CollapsingToolbarLayout = findViewById(R.id.collapsingToolbar)
collapsingToolbar.title = "CoordinatorLayout Demo"
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = SimpleStringRecyclerViewAdapter(getListData())
val fab: FloatingActionButton = findViewById(R.id.fab)
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
}
private fun getListData(): List {
return (1..50).map { "Item $it" }
}
}
Step 7: Implement a Simple RecyclerView Adapter in Kotlin
Here’s a basic adapter to populate RecyclerView with data:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class SimpleStringRecyclerViewAdapter(private val dataSet: List) :
RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val textView: TextView = view.findViewById(android.R.id.text1)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(android.R.layout.simple_list_item_1, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.text = dataSet[position]
}
override fun getItemCount() = dataSet.size
}
Custom Behaviors
You can create custom behaviors to define specific interactions between views. To create a custom behavior, extend the CoordinatorLayout.Behavior class and override its methods to react to specific events. Let’s create a custom behavior for the FAB:
Step 1: Create Custom Behavior Class
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
import androidx.core.view.ViewCompat
class FabHideOnScrollBehavior(context: Context, attrs: AttributeSet? = null) :
CoordinatorLayout.Behavior<FloatingActionButton>(context, attrs) {
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
}
override fun onNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int,
consumed: IntArray
) {
if (dyConsumed > 0) {
child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
override fun onHidden(fab: FloatingActionButton) {
super.onHidden(fab)
fab.visibility = View.INVISIBLE
}
})
} else if (dyConsumed < 0) {
child.show()
}
}
}
Step 2: Apply Custom Behavior to FAB
In your XML, specify the custom behavior for the FAB:
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:focusable="true"
android:src="@android:drawable/ic_dialog_email"
app:layout_anchor="@id/appBar"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior=".FabHideOnScrollBehavior" />
Now, the FAB will hide when you scroll down and show when you scroll up.
Conclusion
CoordinatorLayout is an essential tool for building sophisticated UIs in Android with XML. By understanding how to use CoordinatorLayout with AppBarLayout, CollapsingToolbarLayout, RecyclerView, and custom behaviors, you can create modern, interactive, and visually appealing applications. While new UI technologies like Jetpack Compose offer alternative approaches, mastering CoordinatorLayout remains valuable for maintaining and developing apps that rely on XML layouts.