Search code examples
androidandroid-layoutmaterial-designandroid-toolbarmaterial-components-android

Transparent toolbar on scrolling with shadow effect


I'm trying to get an effect similar to that of the toolbar in the google messages app.

Basically the toolbar has a solid color and when the user scrolls it becomes transparent with a shadow at the bottom.

I managed to get a shadow effect but I can't get the transparent effect.

This is what I'm trying to achieve:enter image description here

And this is what I can get: enter image description here

This is my XML AppBarLayout with toolbar:

<com.google.android.material.appbar.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:liftOnScroll="true">

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:minHeight="?attr/actionBarSize"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fitsSystemWindows="true"
    app:titleTextColor="@color/colorTextMain"
    android:background="@color/colorPrimary">
</androidx.appcompat.widget.Toolbar>

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

Solution

  • Solution

    I managed to get what I wanted! I thought it was easier to get it, however, i write my solution in case it is useful and if someone wants to improve it!

    First, google app messages way to do it.

    When the user does not scroll, toolbar elevation is 0. enter image description here

    When the user scrolls, toolbar elevation is changed and the entire toolbar and status bar (notice status bar) are transparent!

    enter image description here

    How does mine look? enter image description here

    enter image description here

    Let's create a CoordinatorLayout with inside a ToolBar, NestedScrollView and a LinearLayout:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.coordinatorlayout.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_gravity="center_horizontal"
    android:gravity="center_horizontal"
    android:background="@color/colorPrimary">
    
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorToolbar"
        android:elevation="0dp"
        app:titleTextColor="@color/colorTextMain"
        android:fitsSystemWindows="true"/>
    
    <androidx.core.widget.NestedScrollView
        android:id="@+id/myScroll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <LinearLayout
            android:id="@+id/linear_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
    
          ... place page content here ...
    
        </LinearLayout>
    
    </androidx.core.widget.NestedScrollView>
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    

    Now, inside your class OnCreate method

        // find the toolbar view inside the activity layout
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        toolbar.setTitleTextAppearance(this, R.style.FontStyle);
        Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setTitle(getString(R.string.about));
        toolbar.setElevation(1); // required or it will overlap linear layout
        toolbar.setBackgroundColor(Color.TRANSPARENT); // required to delete elevation shadow
    
        // status bar height programmatically , notches...
        statusBarHeight = getStatusBarHeight();
        linearLayout = findViewById(R.id.linear_layout);
        linearLayout.setPadding(1,180+statusBarHeight,1,0);
        ViewGroup.LayoutParams params = toolbar.getLayoutParams();
        params.height = 180+statusBarHeight;
        toolbar.setLayoutParams(params);
    

    Since we have notches ( :-( ), we have to calculate status bar height programatically. By default status bar height is 24dp. Unfortunately not all notch phones use 24dp. How to calculate status bar height programatically

    Now we need to handle toolbar elevation whether the user moves along the page or not. Inside your class OnCreate method

    NestedScrollView scroller = findViewById(R.id.myScroll);
        scroller.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
    
            if (scrollY > oldScrollY) {
                // when user scrolls down set toolbar elevation to 4dp
                toolbar.setElevation(4);
                toolbar.setBackgroundColor(getColor(R.color.colorToolbar));
            }
            if (scrollY < oldScrollY) {
                // when user scrolls up keep toolbar elevation at 4dp
                toolbar.setElevation(4);
                toolbar.setBackgroundColor(getColor(R.color.colorToolbar));
            }
    
            if (scrollY == 0) {
                // if user is not scrolling it means
                // that he is at top of page 
                toolbar.setElevation(1); // required or it will overlap linear layout
                toolbar.setBackgroundColor(Color.TRANSPARENT); // required to delete elevation shadow
            }
        });
    

    Almost done! This is a crucial part! Again inside your class OnCreate method

    switch (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) {
            case Configuration.UI_MODE_NIGHT_YES:
                getWindow().getDecorView().setSystemUiVisibility(
                        View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
                getWindow().setStatusBarColor(Color.TRANSPARENT);
                break;
            case Configuration.UI_MODE_NIGHT_NO:
                getWindow().getDecorView().setSystemUiVisibility(
                        View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                                | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                getWindow().setStatusBarColor(Color.TRANSPARENT);
                break;
        }
    

    Configuration.UI_MODE_NIGHT_YES and Configuration.UI_MODE_NIGHT_NO: are a way to handle dark and light theme. Since we are going to put all the entire content of the page on the status bar we won't have control anymore of status bar icons colors (obtainable via styles) and so we need to detect if a light or dark theme is enabled for programatically set status bar icons colors!