Android - How to make the toolbar fade while still showing the navigation icon

What is this toolbar behaviour, and would I ever need it?

This is a toolbar which fades in or out based on the current offset of the user's scrollable view. As this toolbar keeps the navigation icon visible, there are 2 main advantages.

- The user may navigate back at any time (without needing to use the physical back button) without needing to scroll.
- If you require a gradient background behind the toolbar in the screen's initial state, this toolbar will not block it until the user begins to scroll.

Prerequisites

- Android Studio
- Knowledge of Kotlin / Java
- Knowledge of Scrollable View and Toolbar library

Implementation details

Setting up your view

For this toolbar behaviour, we'll need to make sure that our view contains has a Toolbar and a scrollable view such as a ScrollView or a Recyclerview

In my example, I'll be using a Toolbar and RecyclerView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F1F1F1"
    tools:context=".MainActivity">    

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="@color/white"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navigationIcon="?attr/homeAsUpIndicator"
        app:title="toolbar!" />     

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar" />  
        
</androidx.constraintlayout.widget.ConstraintLayout>

Important things to note:
  • You must set your Toolbar background (XML or programmatically) otherwise the following code will result in a NullPointerException.
  • If you don't set the background colour on your root element and set the toolbar background (XML) to @color/white you will encounter a really weird ghosting issue

Setting the opacity on the toolbar views

To get things started, we'll set the opacity of two views to 0, so the Toolbar can fade in users scroll along. The first view we'll be making transparent is the title TextView. It's a little difficult to obtain said TextView from the Toolbar using traditional method such as findViewById or ViewBinding since it appears that the TextView's are dynamically added. Looking at the Toolbar implementation, at lines 140 and 141 we can see that there are only two TextView's which means we can extract the title of the Toolbar with the following code

//This works since the first child TextView will always be the title TextView
val toolbarText = viewBinding.toolbar.children.firstOrNull { it is TextView }
toolbarText?.alpha = 0f

Up next, we set the toolbar's background opacity. This can be done with the following snippet

viewBinding.toolbar.background.alpha = 0

Setting the scroll listener on the scrollable view

In this section, I'll be explaining the implementation using a RecyclerView but the same concept applies for the ScrollView. Firstly, we add a scroll listener onto the RecyclerView then override the onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int).Next, we do some maths to determine the opacity of the Toolbar TextView and background.

1. Get the y coordinate for the top of the toolbar. We can do this by using the function View.getLocationOnScreen(int[] outLocation) to do this. If you don't know much about this function, here's a quick explanation. The function takes in an Integer array which must be of size 2 and sets the array so that index 0 is the x coordinate and index 1 is the y coordinate.

//1. Get the y coordinate for the top of the toolbar
val toolbarCoordinates = IntArray(2)
viewBinding.toolbar.getLocationOnScreen(toolbarCoordinates)
2. Calculate the distance between the top of the toolbar and the bottom of the view, where you'd like toolbar to be opaque. You can use the same function in step 1 to determine the top y coordinate of the view, then add the height of the view to get the bottom of the y coordinate of said view.
//2. Calculate the distance between the top of the toolbar and the bottom of the
//view. Once this view is no longer visible the toolbar should be opaque
val viewCoordinates = IntArray(2)
val viewHolder = viewBinding.recyclerView.findViewHolderForAdapterPosition(0)
val view = viewHolder?.itemView
view?.getLocationInWindow(viewCoordinates)
if (view != null) {
    //Multiply the toolbar coordinate by 2 since the y coordinate of the toolbar may not be 0 as 
    //the status bar may offset the y coordinate by it's height.
    val distanceBetweenToolbarAndView = viewCoordinates[1] + viewHolder.itemView.height - toolbarCoordinates[1] * 2
...
3. Divide the ScrollView offset by the distance calculated in the step above, which yields a percentage. Set the opacity of the toolbar's title TextView to the percentage then set the opacity of the toolbar's background to the percentage multiplied by 255 (RGB opacity) and that's it!
if (distanceBetweenToolbarAndView > 0) {
    //Divide the vertical offset of the RecyclerView by the distance
    val opacity = (recyclerView.computeVerticalScrollOffset().toFloat() / distanceBetweenToolbarAndView.toFloat())
    toolbarText?.alpha = opacity.coerceIn(0f..1f)
    viewBinding.toolbar.background.alpha = (opacity * 255).toInt().coerceIn(0..255)
} else {
    viewBinding.toolbar.background.alpha = 255
    toolbarText?.alpha = 1f
}
Some important notes

- Make sure that your if conditions have an else statement which makes the toolbar opaque
- This tutorial was designed without any architecture (MVP, MVVM) in mind for simplicity.

Thanks for reading my first tutorial, if you have any questions feel free to reach to me here