28

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.

Kenneth
  • 3,767
  • 8
  • 36
  • 59
  • Can you explain better this: "the ToolBar should scroll out of sight" and "the ToolBar is always scrollable" i can't understand what is the searched result – lubilis Sep 18 '15 at 09:19
  • The result when content in viewpager is scrollable and scrolled: tabs sticks top, toolbar is scrolled out. Result when content in ViewPager is not scrollable: ToolBar on top, TabLayout below. – Kenneth Sep 18 '15 at 09:20
  • 2
    When content int Viewpager is not scrollable, remove app:layout_behavior="@string/appbar_scrolling_view_behavior" – tiny sunlight Nov 01 '15 at 14:32
  • Did you find any good ways to solve this issue? I have the same. – saturov Mar 23 '16 at 18:40

8 Answers8

8

I have solved the issue, tried over the example Google template and find out that

app:layout_behavior="@string/appbar_scrolling_view_behavior" 

line must be added into view pager properties xml. It solved my problem.

Vladimir F
  • 50,383
  • 4
  • 60
  • 96
Mert
  • 113
  • 1
  • 5
7

I suggested you try this sample.

this is a layout like your layout in the sample.

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        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="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            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.support.design.widget.AppBarLayout>

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

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="@dimen/fab_margin"
        android:src="@drawable/ic_done" />

</android.support.design.widget.CoordinatorLayout>
  • 1
    That's pretty much what I already have, and it has the same problem..? – Kenneth Oct 31 '15 at 09:24
  • this sample is a tutorial for your problem. you can implement your layout with the sample pattern. – Mohammad Hossein Gerami Oct 31 '15 at 09:48
  • Please see my answer below. The sample project have the same problem. https://www.dropbox.com/s/16fep4r7linjtnp/sameproblem.mov?dl=0 – Kenneth Nov 01 '15 at 10:01
  • The sample is from Google, so I assume this behaviour is exact what Google expects for Coordinate Layout. In other word, the CoordinateLayout follows Material Design. If you want to have classic way of scrolling, then you should consider RelativeLayout. – SnowWolf Feb 19 '16 at 16:40
3

using ListView as the data for ViewPager? If so, you need listView.setNestedScrollingEnabled(true);

a442509097
  • 27
  • 2
0

Try adding these attributes on TabLayout:

 app:layout_collapseMode="pin"
 app:tabMode="fixed"

And this on AppBarLayout:

 android:fitsSystemWindows="true"

* UPDATE *

I tried and this is not enough, since toolbar is still scrollable. The solution is to make some logic about ViewPager (and its content).

Remove from xml layout file the toolbar scroll_flag attribute. You have to implement some java code to check if ViewPager content height is > then screenHeight - (toolbar + tabBar). If true, set programmatically the scroll_flags as this:

 LayoutParams params;
 params = // get layout params from your toolbar

 params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);

 // set params
 toolbar.setLayoutParams(params);
lubilis
  • 3,357
  • 4
  • 28
  • 49
  • Thank you again, but still not luck. I must add that the content in each ViewPager fragment is of course variable. Some have scrollable content, some don't. – Kenneth Sep 18 '15 at 09:22
  • Ok, now i understand, try with my updated answer Maybe ViewPager should have android:layout_height="match_parent" ? – lubilis Sep 18 '15 at 09:26
  • Hehe. Tried your updated answers, also match_parent on the ViewPager. Still no luck, I'm afraid. Maybe this isn't doable. – Kenneth Sep 18 '15 at 09:55
  • ViewPager content fragment contains ScrollView, ListView or RecyclerView? – lubilis Sep 18 '15 at 10:40
  • Well, at the moment all the content fragments are of type RecyclerView, but not all of them with enough content to scroll vertically. – Kenneth Sep 18 '15 at 10:42
  • Good, RecyclerView has a useful method called 'canScrollVertically(int direction)' that you could use to implement your own logic. If RecyclerView can't scroll, do nothing, else set scroll flags to the toolbar as described in my updated answer – lubilis Sep 18 '15 at 10:46
  • Sry, I've been busy. Will post details soon. – Kenneth Oct 20 '15 at 15:51
  • 1
    I would also like to know :) – kiddouk Oct 22 '15 at 15:49
  • It doesnt really do the trick for me, I now have other views as well. It seems that the height of the Viewpager is actually the height of the whole parent view, not just parentview-toolbar-tabbar. If i remove the appbar_scrolling_view_behavior on the ViewPager the height is normal. Still investigating. – Kenneth Oct 26 '15 at 11:07
  • Hi @lubilis! I have this same issue and I did tried your approach. It works for the first time you open the activity, the height of the viewpager gets calculated correctly (first fragment in pager doesn't need scrolling). But unfortunately, when I go to my other fragment (click on the second tab), it has a recyclerview with many elements and it requires some scrolling so I set scroll flags to the toolbar. The problem is that once you do that, the height of the fragments inside of my viewPager gets recalculated (wrongly, a part of the view gets hidden below the screen) and there's going back. – acrespo Sep 20 '16 at 18:09
  • Hi acrespo, i can't reproduce your issue so it's hard for me to give you a solution. Maybe you could force fragment inside viewPager to not recalculate its size but i don't know if it's possible – lubilis Sep 21 '16 at 07:21
0

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,

Kenneth
  • 3,767
  • 8
  • 36
  • 59
-1

The ViewPager height should be match_parent and not wrap_content.

marcerv
  • 374
  • 1
  • 9
-1

i've just had the same problem. The solution is very simple, just set your viewpager height

android:layout_height="wrap_content"
irfan.k
  • 1
  • 1
-2

Simple solution is - wrap your appbar layour and page viewer with a relative layout. - give your appbar layout some id - in page view set android:layout_below="Your_appbar_layout" Eg:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
    android:id="@+id/your_appBar_ID"
    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"
    android:layout_below="@+id/your_appBar_ID"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</RelativeLayout>