Search code examples
androidlayoutandroid-viewpagercoordinator-layout

Android CoordinatorLayout + AppbarLayout + Viewpager always scrolling


I have a classic layout with a ToolBar on the top, a TabLayout below it, and a ViewPager switching tabs from the TabLayout. When content in the ViewPager is scrollable, the ToolBar should scroll out of sight, and the TabLayout should follow and stick when it reaches the top.

All this is good in my current code, except, the ToolBar is always scrollable, regardless of the size of the ViewPager's content. See my code below. Any brilliant ideas on how to fix this?

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/primary"
        android:orientation="vertical">

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

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:scrollbars="horizontal"
            app:tabIndicatorColor="@color/black_text" />

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

    <android.support.v4.view.ViewPager
        android:id="@+id/tabs_activity_view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

EDIT:

I can see that the viewPager's height is the same as the height for the entire root view. This might be intentded, as the appbar_scrolling_view_behavior does seem to add a top and bottom offset. It does however seem weird, since it will result in always scrolling the toolbar and tabbar.


Solution

  • Based on other samples, my own code, and the (somewhat messy) source code of the appbar_scrolling_view_behavior:

            public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
                View dependency) {
            final CoordinatorLayout.Behavior behavior =
                    ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
            if (behavior instanceof Behavior) {
                // Offset the child so that it is below the app-bar (with any overlap)
    
                final int appBarOffset = ((Behavior) behavior)
                        .getTopBottomOffsetForScrollingSibling();
                final int expandedMax = dependency.getHeight() - mOverlayTop;
                final int collapsedMin = parent.getHeight() - child.getHeight();
    
                if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
                    // If we have an overlap top, and the dependency is an AppBarLayout, we control
                    // the offset ourselves based on the appbar's scroll progress. This is so that
                    // the scroll happens sequentially rather than linearly
                    final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
                    setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
                            Math.abs(appBarOffset) / (float) scrollRange));
                } else {
                    setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset);
                }
            }
            return false;
        }
    

    I'm reading this in a way explaining the problem, as this is expected behavior with this code.

    I think we need to write our own scroll behavior, specially for the RecyclerView,