How to reset the Toolbar position controlled by the CoordinatorLayout?

AndroidNavigation DrawerAndroid Design-LibraryAndroiddesignsupport

Android Problem Overview


The app I'm working on consists of a Navigation Drawer which is implemented in an Activity. The activity layout is as follows:

<FrameLayout 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.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/coordinator"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <include
            android:id="@+id/appbar"
            layout="@layout/appbar" />

    </android.support.design.widget.CoordinatorLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/header_drawer"
        app:menu="@menu/menu_nav">
    </android.support.design.widget.NavigationView>

</android.support.v4.widget.DrawerLayout>

</FrameLayout>

This is a very common pattern, only thing that changes frequently is the Fragment inside the container layout.

If any of the Fragment has a scrolling element, upon scrolling, the CoordinatorLayout will happily make position translations, including the Toolbar/AppBarLayout.

The real problem here is, when the Fragment gets replaced, the position for the Toolbar remains the same, i.e., if the Toolbar is hidden, it will stay so which isn't intended.

Result is this:

This:

After scroll

Gets stuck:

Then change

How can one reset the Toolbar position for this case?

EDIT: A bug is probable, the AppBarLayout offset change listener gets called only when relaunching the app (press back button and open the app), and stops getting called again after an intense fling.

Android Solutions


Solution 1 - Android

To reset the scroll state, just get the AppBarLayout.Behavior object

CoordinatorLayout coordinator = (CoordinatorLayout) findViewById(R.id.coordinator);
AppBarLayout appbar = (AppBarLayout) findViewById(R.id.appbar);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appbar.getLayoutParams();
AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();       

and call onNestedPreScroll method manually:

int[] consumed = new int[2];
behavior.onNestedPreScroll(coordinator, appbar, null, 0, -1000, consumed);

If you would like to reset smoothly with an animation, you can try calling onNestedFling instead:

behavior.onNestedFling(coordinator, appbar, null, 0, -1000, true);

Solution 2 - Android

First get a AppBarLayout refrence in you MainActivity, then in the pause state of the fragment that is being replaced, use the method below to expand toolbar :

MainActivity.appbar.setExpanded(true,true);

And or to close the toolbar :

MainActivity.appbar.setExpanded(false,true);

The second parameter is used to scroll the toolbar smoothly.

Solution 3 - Android

Update your support lib to v23 then you can use:

appBarLayout.setExpanded(true/false);

>public void setExpanded (boolean expanded)

> Sets whether this AppBarLayout is expanded or not, animating if it has already been laid out. > > As with AppBarLayout's scrolling, this method relies on this layout being a direct child of a CoordinatorLayout. > >expanded true if the layout should be fully expanded, false if it should be fully collapsed

Solution 4 - Android

@razzledazzle The AppBarLayout stores onOffsetChangedListeners as WeakReferences, which means they are garbage collected when needed, for instance when you do a intense fling. See solution here:

https://code.google.com/p/android/issues/detail?id=176328

Solution 5 - Android

I'm using this codes before fragment changes.

scrollingElement.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
scrollingElement.dispatchNestedPreScroll(0, -Integer.MAX_VALUE, null, null);
scrollingElement.stopNestedScroll();

Solution 6 - Android

Replace or Wrap the FrameLayout inside the android.support.design.widget.CoordinatorLayout with a android.support.v4.widget.NestedScrollView and that will do the job automatically without having to any other hacks....so it would look like this:

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <include
        android:id="@+id/appbar"
        layout="@layout/appbar" />

</android.support.design.widget.CoordinatorLayout>

Solution 7 - Android

As document said

> AppBarLayout supports
> void setExpanded (boolean expanded, boolean animate);
> Sets whether this AppBarLayout is expanded or not. > > - expanded boolean: true if the layout should be fully expanded, false if it should be fully collapsed
> - animate boolean: Whether to animate to the new state

So first, you need

AppBarLayout appBarLayout = findViewById(R.id.appBarLayout);

then to expand layout

appBarLayout.setExpanded(true, true); // with animation
appBarLayout.setExpanded(true, false); // without animation  

and to collapse layout

appBarLayout.setExpanded(false, true); // with animation
appBarLayout.setExpanded(false, false); // without animation 

Solution 8 - Android

// Get the reference for appbar from layout
AppBarLayout appbar = rootView.findViewById(R.id.appbar);
//boolean expanded, boolean animate
appbar.setExpanded(true, true);

Solution 9 - Android

I had a similar problem. My layout was placed either higher than needed (like here), or lower, when I dynamically resized it. It was a Fragment that was replaced in FrameLayout programmatically. What I did: I began to re-creating my Fragment on change the FrameLayout size.

myFragment = new MyFragment();
getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_layout, myFragment)
                .commit();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrazzledazzleView Question on Stackoverflow
Solution 1 - AndroidMattSkalaView Answer on Stackoverflow
Solution 2 - AndroidShayan ShafieeView Answer on Stackoverflow
Solution 3 - AndroidContextView Answer on Stackoverflow
Solution 4 - AndroidfreebiefalkView Answer on Stackoverflow
Solution 5 - AndroidweumjView Answer on Stackoverflow
Solution 6 - AndroidAlécio CarvalhoView Answer on Stackoverflow
Solution 7 - Androidhassan moradnezhadView Answer on Stackoverflow
Solution 8 - AndroidShashank DegloorkarView Answer on Stackoverflow
Solution 9 - Androidantaki93View Answer on Stackoverflow