cookies

Hi! This website uses cookies. By continuing to browse or by clicking “I agree”, you accept this use. For more information, please see our Privacy Policy

bg

How to build Collapsing Toolbar with MotionLayout

author

Dmytro Ruban

/

Android Developer

4 min read

4 min read

Creating a Collapsing Toolbar can be easily considered an essential part of an engaging design for Android application development. So how can we build it? Well, there are two ways to do it.

  1. The most common: using a CollapsingToolbarLayout inside a CoordinatorLayout to achieve this exact behavior. And it’s totally okay if you already have it implemented and running on your app. Don’t mess up with what’s already working, as they say.
  2. An extra one: using MotionLayout, which provides excellent flexibility and allows achieving exciting results almost effortlessly when developing android applications.

So, in this article, I’ll show you how to create an Android Collapsing Toolbar with MotionLayout.

Android app development

Article content:

  1. A couple of words about MotionLayout
  2. Applying MotionLayout for a Collapsing Toolbar: the process
  3. To wrap up

Disclaimer: For continuing to read the article, just make sure that you’re familiar with MotionLayout and ConstraintLayout basics.

A couple of words about MotionLayout

Basically, MotionLayout is a new class of the ConstraintLayout 2.0 library, which was announced back at Google IO 2018. Since then, it’s already been released to a stable beta version, so it’s become widely available and popular for android applications development.

Fundamentally, the tool helps Android app developers to handle motion and widget animation in their applications. As Nicolas Roard, one of the Google developers working on MotionLayout puts it: in terms of capabilities, it’s like a mix between the property animation framework, TransitionManager, and CoordinatorLayout.

MotionLayout is primarily used in mobile application development when animating the UI elements a user will interact with. The second but not less critical purpose of applying the tool is to help users understand what your app actually does.

Now that we have a general meaning of MotionLayout and its possibilities, we should deal with the flesh implementation of a Collapsing Toolbar Android apps usually have.

Applying MotionLayout for a Collapsing Toolbar: the process

The building process has two main parts. Let’s tap a toe into each of them step by step.

Layouts part

First, let’s see how our layout should look like:

<androidx.coordinatorlayout.widget.CoordinatorLayout>

    // This is our scrollable content: RecyclerView or NestedScrollView, etc.
    <androidx.recyclerview.widget.RecyclerView/>

    <com.google.android.material.appbar.AppBarLayout>

        <androidx.constraintlayout.motion.widget.MotionLayout
            // Inside MotionLayout, we will place our collapsing toolbar content
        </androidx.constraintlayout.motion.widget.MotionLayout>

    </com.google.android.material.appbar.AppBarLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Now we have to dive deeper into each component in our tree.

For proper content scrolling `app:layout_behavior=”@string/appbar_scrolling_view_behavior` must be set on the scrolling sibling (e.g., `NestedScrollView`, `RecyclerView`, etc.).

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

AppBarLayout must have a fixed height of an expanded state. Otherwise, MotionLayout behaves unpredictably. Please, keep this in mind as this is important.

<com.google.android.material.appbar.AppBarLayout
    android:id="@+id/app_bar_layout"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:background="@android:color/white">
  
    <androidx.constraintlayout.motion.widget.MotionLayout/>
      
</com.google.android.material.appbar.AppBarLayout>

</com.google.android.material.appbar.AppBarLayout>

The next thing to do is to set `layout_height` to `match_parent` to keep MotionLayout a fixed height. Attribute `minHeight` represents our collapsed toolbar height. We have to set it to system action bar size just to follow the same size to all collapsed toolbars across the app. However, you can set up any size you want.

Now, let’s proceed further. `layoutDescription` is a simple MotionScene with a start and end `ConstraintSets.`
Please note that this part doesn’t require any special activities. We don’t need to add any transitions triggers because the animation will be triggered programmatically after setting `layout_scrollFlags` the same as with the implementation of `CollapsingToolbarLayout.`

<androidx.constraintlayout.motion.widget.MotionLayout
    android:id="@+id/motion_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minHeight="?attr/actionBarSize"
    app:layoutDescription="@xml/fragment_collapsing_motion_layout_scene"
    app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

</androidx.constraintlayout.motion.widget.MotionLayout>

The biggest trick here is a proper transition between the expanded and collapsed state of our toolbar content.
To make it smooth and non-lagging, we need to define two guidelines: one for a collapsed state and another one for expanded.

The first guideline will be a guideline for insets. It must be constrained to begin. Thus, all content of the expanded toolbar must be constrained to it like to the top of the parent. Consequently, we align all content from the start `ConstraintSet` to `insets_guideline` like to the top of the parent.

As we need setup insets in runtime, to have a look at a proper render in preview, let’s set `tools:layout_constraintGuide_begin=”24dp”.

<androidx.constraintlayout.widget.Guideline
     android:id="@+id/insets_guideline"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="horizontal"
     tools:layout_constraintGuide_begin="24dp" />

Just as `insets_guideline` works as the top for the expanded state, our second guideline will serve as the bottom for the collapsed state and should be constrained to the end of the parent.

<androidx.constraintlayout.widget.Guideline
     android:id="@+id/collapsed_top_guideline"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="horizontal"
     tools:layout_constraintGuide_end="?attr/actionBarSize" />

Behind the scene

Now we have to connect `motionLayout` with `appBarLayout` to trigger animation through `OnOffsetChangedListener`. Every time the Collapsing Toolbar should change its collapsed progress, it will inform us by just converting `verticalOffset` to a state of animation between 0f and 1f. It will allow us to inform MotionLayout about the new progress. This action will be called every time the user scrolls an app.

binding.appBarLayout.addOnOffsetChangedListener(
    AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
        val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
        binding.motionLayout.progress = seekPosition
    }
)

And finally, we set up guidelines with insets.

// Desired collapsed height of toolbar
val toolBarHeight = binding.motionLayout.minimumHeight
ViewCompat.setOnApplyWindowInsetsListener(requireView()) { v, insets ->
    // Resizing motion layout in a collapsed state with needed insets to not overlap    system bars
    val insetHeight = insets.systemWindowInsetTop
    binding.motionLayout.minimumHeight = toolBarHeight + insetHeight

    // Update guidelines with givens insets
    val startConstraintSet = binding.motionLayout.getConstraintSet(R.id.start)
    startConstraintSet.setGuidelineBegin(R.id.insets_guideline, insetHeight)
    val endConstraintSet = binding.motionLayout.getConstraintSet(R.id.end)
    endConstraintSet.setGuidelineBegin(R.id.insets_guideline, insetHeight)
    endConstraintSet.setGuidelineEnd(R.id.collapsed_top_guideline, toolBarHeight )
    insets
}

To wrap up

Well, as we see, MotionLayout is pretty good at handling a Collapsing Toolbar. There are a few more reasons to include MotionLayout into your animations.

  1. It allows animating any layout property, making any interaction between a user and your app even more engaged.
  2. MotionLayout empowers you to customize transitions to cover a wide range of UI needs.
  3. Its declarative nature, which means that any of the transitions can be described in XML, regardless of their complexity, simplifies building code-less animations.

All those above significantly enhance graphical tooling in Android Studio, as the core MotionLayout developers state.

Well, I hope that my thoughts and experiments were somehow helpful for you, and now you have a clear perception of how to build a Collapsing Toolbar for your app.

Anyway, if you have any questions regarding this matter or any other topic in mobile app development for Android, please feel free to contact NERDZ LAB as your trustworthy Android app development company.