How to make the Toolbar snap into view or out of view when using Google Design Library?

19,043

Solution 1

This feature has been added in 23.1.0 version of android support library. From release notes:

Added edge snapping support to the AppBarLayout class by adding the SCROLL_FLAG_SNAP constant. When scrolling ends, if the view is only partially visible, the view is snapped and scrolled to its closest edge.

<android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

           <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:layout_scrollFlags="scroll|enterAlways|snap" />
           -----
           -----

For more info: http://android-developers.blogspot.in/2015/10/android-support-library-231.html

Solution 2

EDIT: as of support 23.1.0 this is no longer needed. See this answer instead.

One possible way to solve this is customizing the Behavior set to your AppBarLayout.

<android.support.design.widget.AppBarLayout
    app:layout_behavior="com.myapp.AppBarLayoutSnapBehavior"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    ...

Your AppBarLayoutSnapBehavior would change the default behavior of AppBarLayout.Behavior, by adding the snap logic when the scroll stops. Hopefully, the code below is self explanatory.

package com.myapp;

public class AppBarLayoutSnapBehavior extends AppBarLayout.Behavior {

    private ValueAnimator mAnimator;
    private boolean mNestedScrollStarted = false;

    public AppBarLayoutSnapBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
                                       View directTargetChild, View target, int nestedScrollAxes) {
        mNestedScrollStarted = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
        if (mNestedScrollStarted && mAnimator != null) {
            mAnimator.cancel();
        }
        return mNestedScrollStarted;
    }

    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target) {
        super.onStopNestedScroll(coordinatorLayout, child, target);

        if (!mNestedScrollStarted) {
            return;
        }

        mNestedScrollStarted = false;

        int scrollRange = child.getTotalScrollRange();
        int topOffset = getTopAndBottomOffset();

        if (topOffset <= -scrollRange || topOffset >= 0) {
            // Already fully visible or fully invisible
            return;
        }

        if (topOffset < -(scrollRange / 2f)) {
            // Snap up (to fully invisible)
            animateOffsetTo(-scrollRange);
        } else {
            // Snap down (to fully visible)
            animateOffsetTo(0);
        }
    }

    private void animateOffsetTo(int offset) {
        if (mAnimator == null) {
            mAnimator = new ValueAnimator();
            mAnimator.setInterpolator(new DecelerateInterpolator());
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    setTopAndBottomOffset((int) animation.getAnimatedValue());
                }
            });
        } else {
            mAnimator.cancel();
        }

        mAnimator.setIntValues(getTopAndBottomOffset(), offset);
        mAnimator.start();
    }
}

The only thing is, the scroll view (in my case a RecyclerView) snaps along with the Toolbar. I actually like it this way, but I'm not sure that's what you want.

Share:
19,043
Linxy
Author by

Linxy

android email: [email protected]

Updated on June 05, 2022

Comments

  • Linxy
    Linxy almost 2 years

    I am trying to achieve an effect like WhatsApp has, where the Toolbar (when scrolled) will clip into view magnetlike, or out of view magnetlike.

    What I have im my MainActivity XML:

    • DrawerLayout - Base Layout
    • CoordinatorLayout - Layout for the Appbar and Toolbar and Tabs
    • AppBarLayout - For holding Toolbar and Tabs
    • Toolbar - has THIS flag: app:layout_scrollFlags="scroll|enterAlways"
    • SlidingTabLayout - Displays tabs
    • ViewPager - For tabs
    • RecyclerView - For coordinatorlayout

    Now dont get me wrong, it works, when I scroll down the toolbar gets pushed out of view but say I stop scrolling halfway, then the toolbar just sits there half hidden out of view and the other half in view..

    How can I approach solving this problem, as I want it to either snap out of view or into view.