Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collapse content instead of scrolling when CollapsingToolbarLayout is expanded

I've created a simple project with Android's CollapsingToolbarLayout, this is its structure (full layout source on the bottom of the question):

CoordinatorLayout
  AppBarLayout
    CollapsingToolbarLayout
      Toolbar
  RelativeLayout (layout_behavior="@string/appbar_scrolling_view_behavior")
    TextView (layout_alignParentTop="true")
    TextView (layout_alignParentBottom="true")

When I'm expanding/collapsing the bar, the view is scrolled and bottom view goes beyond the screen: enter image description here

What I'm going to achieve is collapsing/shrinking (instead of scrolling) the content (my RelativeLayout) in the way that bottom text always stays visible:

enter image description here enter image description here

How can I do it on the Android?

This is my full layout:

<?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:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:toolbarId="@+id/toolbar">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

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

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:text="The text on the top"
            android:textSize="32sp"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="The text on the bottom"
            android:textSize="32sp"/>
    </RelativeLayout>

 </android.support.design.widget.CoordinatorLayout>
like image 789
Piotr Aleksander Chmielowski Avatar asked Nov 02 '17 13:11

Piotr Aleksander Chmielowski


People also ask

How do you collapse AppBarLayout?

Use mAppBarLayout. setExpanded(true) to expand Toolbar and use mAppBarLayout. setExpanded(false) to collapse Toolbar.

How do I disable scrolling in collapsing Toolbar layout Android?

The solution is simple, we just need to set the app:scrimAnimationDuration=”0" in our collapsing toolbar layout like the below code snippet. Now just run the code and see the results, you will see then there will be no fading animation anymore.

What is collapsing Toolbar Android?

Android CollapsingToolbarLayout is a wrapper for Toolbar which implements a collapsing app bar. It is designed to be used as a direct child of a AppBarLayout. This type of layout is commonly seen in the Profile Screen of the Whatsapp Application.


2 Answers

The real issue here is how to get the RelativeLayout sized properly when the app bar changes height. Before looking at how to do that, we need to make changes to the XML layout.

From the documentation for AppBarLayout:

AppBarLayout also requires a separate scrolling sibling in order to know when to scroll.

Since the RelativeLayout` does not qualify as a "scrolling sibling," we need to encase it in one such as "NestedScrollView." Here is the updated XML file:

activity_main.xml

<android.support.design.widget.CoordinatorLayout 
    android:id="@+id/coordinatorLayoutProfile"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

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

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="220dp"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?android:attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

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

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

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

        <RelativeLayout
            android:id="@+id/relativeLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/holo_blue_light">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:text="The text on the top"
                android:textSize="32sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:text="The text on the bottom"
                android:textSize="32sp" />
        </RelativeLayout>
    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

I may have made a few other changes to the XML to make things work in my environment, but there should be no substantive changes other than inserting NestedScrollView.

Changes to the size of the the RelativeLayout will be set in code and will be triggered by an AppBarLayout.OnOffsetChangedListener. We will compute and change the size of the RelativeLayout based on the vertical offset of the app bar.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private RelativeLayout mRelativeLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mRelativeLayout = findViewById(R.id.relativeLayout);
        final AppBarLayout appBarLayout = findViewById(R.id.appBar);
        final int screenHeight = getResources().getDisplayMetrics().heightPixels;

        appBarLayout.post(new Runnable() {
            @Override
            public void run() {
                adjustRelLayoutHeight(mRelativeLayout, screenHeight - appBarLayout.getBottom());
            }
        });

        appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                adjustRelLayoutHeight(mRelativeLayout, screenHeight - appBarLayout.getBottom());
            }
        });
    }

    private void adjustRelLayoutHeight(RelativeLayout layout, int newHeight) {
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) layout.getLayoutParams();
        lp.height = newHeight;
        layout.setLayoutParams(lp);
    }

    private static final String TAG = "MainActivity";
}

Here is the result:

enter image description here

Update: The following is an alternate approach that uses ViewTreeObserver.OnPreDrawListener() that avoids an extra draw. The effect is the same however:

MainActivity.java (alternate method)

public class MainActivity extends AppCompatActivity {
    private RelativeLayout mRelativeLayout;
    private int mCoordinatorHeight;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mRelativeLayout = findViewById(R.id.relativeLayout);
        final AppBarLayout appBarLayout = findViewById(R.id.appBar);

        final CoordinatorLayout coordinatorLayout =
            (CoordinatorLayout) findViewById(R.id.coordinatorLayoutProfile);
        // Wait for just before drawing the view to get its height.
        coordinatorLayout.getViewTreeObserver()
            .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // Remove the listener so we don't go into an infinite loop.
                    coordinatorLayout.getViewTreeObserver().removeOnPreDrawListener(this);
                    mCoordinatorHeight = coordinatorLayout.getHeight();
                    adjustRelLayoutHeight(mRelativeLayout, mCoordinatorHeight - appBarLayout.getBottom());
                    return false; // abort drawing
                }
            });

        appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                adjustRelLayoutHeight(mRelativeLayout, mCoordinatorHeight - appBarLayout.getBottom());
            }
        });
    }

    private void adjustRelLayoutHeight(RelativeLayout layout, int newHeight) {
        if (newHeight <= 0) {
            // no-op if height is invalid
            return;
        }
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) layout.getLayoutParams();
        if (lp.height != newHeight) {
            lp.height = newHeight;
            layout.setLayoutParams(lp);
        }
    }

    private static final String TAG = "MainActivity";
}
like image 89
Cheticamp Avatar answered Nov 09 '22 11:11

Cheticamp


Instead of using RelativeLayout with layout_behavior="@string/appbar_scrolling_view_behavior" you should choose either NestedScrollingView or RecyclerViw to get everything works as expected.

As it’s stated here

The scrolling content should be placed in a RecyclerView, NestedScrollView, or another view that supports nested scrolling.

Considering your layout I guess you need to place your RelativeLayout inside NestedScrollingView and assign layout_behavior to NestedScrollingView and remove layout_behavior from RelativeLayout.

<?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:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:toolbarId="@+id/toolbar">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

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

    <NestedScrollView android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height= "match_parent">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:text="The text on the top"
                android:textSize="32sp"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:text="The text on the bottom"
                android:textSize="32sp"/>
        </RelativeLayout>
    </NestedScrollView>

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

EDIT 11/4/2017 4:58PM

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:fitsSystemWindows="true">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:fitsSystemWindows="true">

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

            <android.support.design.widget.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                app:contentScrim="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:toolbarId="@+id/toolbar">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

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


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

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

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentTop="true"
                    android:text="The text on the top"
                    android:textSize="32sp"/>

            </RelativeLayout>

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

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="The text on the bottom"
        android:textSize="32sp"/>

</LinearLayout>

Now it works as it's shown below

Result View

like image 34
Eugene Brusov Avatar answered Nov 09 '22 12:11

Eugene Brusov