62

I have an AppBarLayout that scrolls off screen when scrolling a RecyclerView. Below the RecyclerView there is a RelativeLayout that is a footer.

The footer is shown only after scrolling up - it behave like it has

layout_scrollFlags="scroll|enterAlways"

but it doesn't have any scroll flags - is it a bug or am I doing something wrong? I want it to be always visible

before scroll

enter image description here

after scroll

enter image description here

Update

opened a google issue on this - it was marked 'WorkingAsIntended' this still doesn't help because I want a working solution of a footer inside a fragment.

Update 2

you can find the activity and the fragment xmls here -

note that if line 34 in activity.xml - the line containing app:layout_behavior="@string/appbar_scrolling_view_behavior" is commented out the text end is visible from the start - otherwise, it is visible only after scrolling up

Noa Drach
  • 2,381
  • 3
  • 26
  • 44
  • I wasted half of the day trying to figure out whats going on. Thanks to finding your question, I just get rid off CoordinatorLayout. And everything works as intended – Rafael Dec 15 '16 at 08:34
  • I have similar issue, can you please help me : https://stackoverflow.com/questions/47221647/footer-scrolls-off-screen-when-used-in-coordinatorlayout – chetan Nov 10 '17 at 11:40

9 Answers9

52

I use a simplified version of Learn OpenGL ES's solution -- which improves on Noa's solution. It works fine for my simple quick-return toolbar above a TabLayout with footer buttons in each tab's ViewPager content.

Just set the FixScrollingFooterBehavior as the layout_behavior on the View/ViewGroup you want to keep aligned at the bottom of the screen.

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.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?android:attr/actionBarSize"
                android:minHeight="?android:attr/actionBarSize"
                app:title="Foo"
                app:layout_scrollFlags="scroll|enterAlways|snap"
                />

            <android.support.design.widget.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tabMode="fixed"/>

    </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="com.spreeza.shop.ui.widgets.FixScrollingFooterBehavior"
        />

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

Behavior:

public class FixScrollingFooterBehavior extends AppBarLayout.ScrollingViewBehavior {

    private AppBarLayout appBarLayout;

    public FixScrollingFooterBehavior() {
        super();
    }

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

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {

        if (appBarLayout == null) {
            appBarLayout = (AppBarLayout) dependency;
        }

        final boolean result = super.onDependentViewChanged(parent, child, dependency);
        final int bottomPadding = calculateBottomPadding(appBarLayout);
        final boolean paddingChanged = bottomPadding != child.getPaddingBottom();
        if (paddingChanged) {
            child.setPadding(
                child.getPaddingLeft(),
                child.getPaddingTop(),
                child.getPaddingRight(),
                bottomPadding);
            child.requestLayout();
        }
        return paddingChanged || result;
    }


    // Calculate the padding needed to keep the bottom of the view pager's content at the same location on the screen.
    private int calculateBottomPadding(AppBarLayout dependency) {
        final int totalScrollRange = dependency.getTotalScrollRange();
        return totalScrollRange + dependency.getTop();
    }
}
spaaarky21
  • 6,524
  • 7
  • 52
  • 65
jhavatar
  • 3,236
  • 1
  • 18
  • 11
18

Update

The solution below doesn't work for 5.1 as it works in 5 - instead of getTop use getTranslationY in any of the calculations you do.

layout.getTop()-->(int)layout.getTranslationY()
appbar.getTop()+toolbar.getHeight()-->(int)(appbar.getTranslationY()+toolbar.getHeight())

Update 2 with the new support library - 22.2.1 - there is no diff between 5.1 and prev versions, you should only use getTop and ignore the previous update in this answer

Original solution After looking into many directions turns out the solution is actually simple - add paddingBottom to the fragment and adjust it as the page scrolls.

The padding is needed to cover for the changes in the toolbar y position - the coordinator layout is moving the entire page up and down as the toolbar disappears and reappears.

This can be achieved by extending AppBarLayout.ScrollingViewBehavior and setting this as the behavior of the fragment element of the activity.

Here are the basics of the code - it works for an activity with only a toolbar - you can replace it with appbar.getTop() + toolbar.getHeight() and this will work better if your appbar includes tabs.

activity.xml

<android.support.design.widget.CoordinatorLayout
android:id="@+id/main"
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.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="3dp"
    app:elevation="3dp">
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_scrollFlags="scroll|enterAlways"
        />
</android.support.design.widget.AppBarLayout>
<fragment
    android:id="@+id/fragment"
    android:name="com.example.noa.footer2.MainActivityFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="com.example.noa.footer2.MyBehavior"
    tools:layout="@layout/fragment"/>
</android.support.design.widget.CoordinatorLayout>

fragment.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="48dp"
            android:background="@android:color/holo_green_dark"
            tools:context=".MainActivityFragment">
<android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@null"/>
<View
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_alignParentBottom="true"
    android:background="@android:color/holo_red_light"/>
</RelativeLayout>

MainActivityFragment#onActivityCreated

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        CoordinatorLayout.LayoutParams lp = (LayoutParams) getView().getLayoutParams();
        MyBehavior behavior = (MyBehavior) lp.getBehavior();
        behavior.setLayout(getView());
    }

MyBehavior

public class MyBehavior extends AppBarLayout.ScrollingViewBehavior {

    private View layout;

    public MyBehavior() {
    }

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

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        boolean result = super.onDependentViewChanged(parent, child, dependency);
        if (layout != null) {
            layout.setPadding(layout.getPaddingLeft(), layout.getPaddingTop(), layout
                .getPaddingRight(), layout.getTop());
        }
        return result;
    }

    public void setLayout(View layout) {
        this.layout = layout;
    }
}
Noa Drach
  • 2,381
  • 3
  • 26
  • 44
  • 2
    getting class cast exception on this line CoordinatorLayout.LayoutParams lp = (LayoutParams) getView().getLayoutParams(); – Vihaan Verma Sep 01 '15 at 13:12
  • @VihaanVerma - what is the exception? – Noa Drach Sep 01 '15 at 13:26
  • which is the import for LayoutParams used for casting? When using CoordinatorLayout.LayoutParams for casting it gives class cast exception for relative layout( root view in the layout) – Vihaan Verma Sep 01 '15 at 13:54
  • CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) pager.getLayoutParams(); – Noa Drach Sep 01 '15 at 14:56
  • not sure it matters - I copied this line from my current code and the code in this post is from a sample project i created - if you do `CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) getView().getLayoutParams();` you get a class cast exception? – Noa Drach Sep 02 '15 at 08:57
  • 2
    Unfortunately has an issue during animation (when you swipe and let it animate the expand/collapse of the toolbar). – Learn OpenGL ES Oct 27 '15 at 18:31
  • Worked for me. I also had a TabLayout in the header, so I modified the behaviour so that the botom padding is set as: "layout.getTop()-tabs.getHeight()". I'm just getting a bit of visual-lag sometimes – eddie Nov 03 '15 at 01:54
  • The problem is still or has other solution? Please visit: http://stackoverflow.com/questions/35576290/fix-bottom-bar-in-coordinatorlayout – Hanzo Feb 23 '16 at 13:04
  • @Hanzo from the reply I got on the issue I opened in google and the amount of traffic on this post I assume that the problem is still there and doesn't look like it is going to be fixed – Noa Drach Feb 23 '16 at 14:08
  • @Noa Drach. Thanks. I'm gonna try your patch.If It doesn't convince me, I will try other composition – Hanzo Feb 23 '16 at 15:25
  • This is the Class Cast Exception I'm getting: android.widget.FrameLayout$LayoutParams cannot be cast to android.support.design.widget.CoordinatorLayout$LayoutParams Does anybody know how to solve this? – Kim Montano Mar 02 '16 at 11:09
18

I started out with Noa's solution (https://stackoverflow.com/a/31140112/1317564) and it worked for finger drags, but I was running into trouble with flings. After spending some time to trace the method calls and trying out different ideas, here is the solution I ended up with:

// Workaround for https://code.google.com/p/android/issues/detail?id=177195
// Based off of solution originally found here: https://stackoverflow.com/a/31140112/1317564
@SuppressWarnings("unused")
public class CustomScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {
    private AppBarLayout appBarLayout;
    private boolean onAnimationRunnablePosted = false;

    @SuppressWarnings("unused")
    public CustomScrollingViewBehavior() {

    }

    @SuppressWarnings("unused")
    public CustomScrollingViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        if (appBarLayout != null) {
            // We need to check from when a scroll is started, as we may not have had the chance to update the layout at
            // the start of a scroll or fling event.
            startAnimationRunnable(child, appBarLayout);
        }
        return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public boolean onMeasureChild(CoordinatorLayout parent, final View child, int parentWidthMeasureSpec, int widthUsed,
                                  int parentHeightMeasureSpec, int heightUsed) {
        if (appBarLayout != null) {
            final int bottomPadding = calculateBottomPadding(appBarLayout);
            if (bottomPadding != child.getPaddingBottom()) {
                // We need to update the padding in onMeasureChild as otherwise we won't have the correct padding in
                // place when the view is flung, and the changes done in onDependentViewChanged will only take effect on
                // the next animation frame, which means it will be out of sync with the new scroll offset. This is only
                // needed when the view is flung -- when dragged with a finger, things work fine with just
                // implementing onDependentViewChanged().
                child.setPadding(child.getPaddingLeft(), child.getPaddingTop(), child.getPaddingRight(), bottomPadding);
            }
        }

        return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, final View child, final View dependency) {
        if (appBarLayout == null)
            appBarLayout = (AppBarLayout) dependency;

        final boolean result = super.onDependentViewChanged(parent, child, dependency);
        final int bottomPadding = calculateBottomPadding(appBarLayout);
        final boolean paddingChanged = bottomPadding != child.getPaddingBottom();
        if (paddingChanged) {
            // If we've changed the padding, then update the child and make sure a layout is requested.
            child.setPadding(child.getPaddingLeft(),
                    child.getPaddingTop(),
                    child.getPaddingRight(),
                    bottomPadding);
            child.requestLayout();
        }

        // Even if we didn't change the padding, if onDependentViewChanged was called then that means that the app bar
        // layout was changed or was flung. In that case, we want to check for these changes over the next few animation
        // frames so that we can ensure that we capture all the changes and update the view pager padding to match.
        startAnimationRunnable(child, dependency);
        return paddingChanged || result;
    }

    // Calculate the padding needed to keep the bottom of the view pager's content at the same location on the screen.
    private int calculateBottomPadding(AppBarLayout dependency) {
        final int totalScrollRange = dependency.getTotalScrollRange();
        return totalScrollRange + dependency.getTop();
    }

    private void startAnimationRunnable(final View child, final View dependency) {
        if (onAnimationRunnablePosted)
            return;

        final int onPostChildTop = child.getTop();
        final int onPostDependencyTop = dependency.getTop();
        onAnimationRunnablePosted = true;
        // Start looking for changes at the beginning of each animation frame. If there are any changes, we have to
        // ensure that layout is run again so that we can update the padding to take the changes into account.
        child.postOnAnimation(new Runnable() {
            private static final int MAX_COUNT_OF_FRAMES_WITH_NO_CHANGES = 5;
            private int previousChildTop = onPostChildTop;
            private int previousDependencyTop = onPostDependencyTop;
            private int countOfFramesWithNoChanges;

            @Override
            public void run() {
                // Make sure we request a layout at the beginning of each animation frame, until we notice a few
                // frames where nothing changed.
                final int currentChildTop = child.getTop();
                final int currentDependencyTop = dependency.getTop();
                boolean hasChanged = false;

                if (currentChildTop != previousChildTop) {
                    previousChildTop = currentChildTop;
                    hasChanged = true;
                    countOfFramesWithNoChanges = 0;
                }
                if (currentDependencyTop != previousDependencyTop) {
                    previousDependencyTop = currentDependencyTop;
                    hasChanged = true;
                    countOfFramesWithNoChanges = 0;
                }
                if (!hasChanged) {
                    countOfFramesWithNoChanges++;
                }
                if (countOfFramesWithNoChanges <= MAX_COUNT_OF_FRAMES_WITH_NO_CHANGES) {
                    // We can still look for changes on subsequent frames.
                    child.requestLayout();
                    child.postOnAnimation(this);
                } else {
                    // We've encountered enough frames with no changes. Do a final layout request, and don't repost.
                    child.requestLayout();
                    onAnimationRunnablePosted = false;
                }
            }
        });
    }
}

I'm not a fan of rechecking the layout on every animation frame, and this solution isn't perfect as I've seen some issues if programmatically expanding/collapsing the app bar layout, but for now I haven't found a better solution. The performance is fine on a new device and acceptable on an older device. If someone else does, please feel free to take my answer as a source and repost.

Community
  • 1
  • 1
Learn OpenGL ES
  • 4,759
  • 1
  • 36
  • 38
  • Thank you. This is the best solution I had found so far. However, it is not perfect yet as it does yield flickering effect. Please refer to http://stackoverflow.com/questions/36098350/how-to-fix-footer-properly-which-is-inside-coordinatorlayout for more explanation on the problem. – Cheok Yan Cheng Mar 19 '16 at 05:47
  • 1
    I found a way, without affecting footer yet able to hide toolbar, by not using CoordinatorLayout - http://stackoverflow.com/a/36122127/72437 – Cheok Yan Cheng Mar 21 '16 at 02:17
  • I tried your solution. It works. But it left an additional padding down to my footer. Can't figure out the reason. Any idea how it would have happened? – Midhun Vijayakumar Mar 26 '16 at 08:14
  • Additional padding is there in other fragments too and it has no footer. – Midhun Vijayakumar Mar 26 '16 at 08:17
  • Got it. I had to remove statusbarheight from 21 and above devices. Thank you. – Midhun Vijayakumar Mar 26 '16 at 09:07
  • 1
    Spent a few days getting up to 50 rep so I could comment and thank you for this answer. This is the correct answer. The other one has bugs in a number of situations (leave the fragment and then go back, etc.). This fixed all of my bugs related to this issue. – rexar5 Sep 12 '18 at 20:56
  • Why do I get a Caused by android.view.InflateException .....could not inflate class MyCustomScrollingBehavior on some devices while using this code? – Beast77 Jun 07 '19 at 07:20
1
package pl.mkaras.utils;

import android.content.Context;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;

public class ScrollViewBehaviorFix extends AppBarLayout.ScrollingViewBehavior {

    public ScrollViewBehaviorFix() {
        super();
    }

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

    public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
                                  int heightUsed) {
        if (child.getLayoutParams().height == -1) {
            List<View> dependencies = parent.getDependencies(child);
            if (dependencies.isEmpty()) {
                return false;
            }

            final AppBarLayout appBar = findFirstAppBarLayout(dependencies);
            if (appBar != null && ViewCompat.isLaidOut(appBar)) {
                int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
                if (availableHeight == 0) {
                    availableHeight = parent.getHeight();
                }

                final int height = availableHeight - appBar.getMeasuredHeight();
                int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);

                parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
                int childContentHeight = getContentHeight(child);

                if (childContentHeight <= height) {
                    updateToolbar(parent, appBar, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed, false);

                    heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
                    parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);

                    return true;
                } else {
                    updateToolbar(parent, appBar, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed, true);

                    return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
                }
            }
        }

        return false;
    }

    private static int getContentHeight(View view) {
        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;

            int contentHeight = 0;
            for (int index = 0; index < viewGroup.getChildCount(); ++index) {
                View child = viewGroup.getChildAt(index);
                contentHeight += child.getMeasuredHeight();
            }
            return contentHeight;
        } else {
            return view.getMeasuredHeight();
        }
    }

    private static AppBarLayout findFirstAppBarLayout(List<View> views) {
        int i = 0;

        for (int z = views.size(); i < z; ++i) {
            View view = views.get(i);
            if (view instanceof AppBarLayout) {
                return (AppBarLayout) view;
            }
        }

        throw new IllegalArgumentException("Missing AppBarLayout in CoordinatorLayout dependencies");
    }

    private void updateToolbar(CoordinatorLayout parent, AppBarLayout appBar, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
                               int heightUsed, boolean toggle) {
        toggleToolbarScroll(appBar, toggle);

        appBar.forceLayout();
        parent.onMeasureChild(appBar, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
    }

    private void toggleToolbarScroll(AppBarLayout appBar, boolean toggle) {
        for (int index = 0; index < appBar.getChildCount(); ++index) {
            View child = appBar.getChildAt(index);

            if (child instanceof Toolbar) {
                Toolbar toolbar = (Toolbar) child;
                AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
                int scrollFlags = params.getScrollFlags();

                if (toggle) {
                    scrollFlags |= AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
                } else {
                    scrollFlags &= ~AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
                }

                params.setScrollFlags(scrollFlags);
            }
        }
    }
}

This behavior basically removes scroll flag SCROLL from AppBarLayout, when scrolling content in dependent view (RecyclerView, NestedScrollView) is less than view height, ie. when scrolling is not needed. It also overrides offsetting scrolling view, which is normally done by AppBarLayout.ScrollingViewBehavior. Works well when adding footer, ie. button, to scrolling view or in ViewPager, where content length may be different in each page.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
TrueCurry
  • 205
  • 3
  • 12
  • Does this work with scroll|enterAlways|snap scroll flags in Toolbar? How can I make my toolbar scrolls with this approach? – Yuri Heupa Jun 13 '16 at 14:05
  • @YuriHeupa it does work with other scroll flags. This approach just removes or adds 'SCROLL' flag and does not alter other flags. [When there is no 'SCROLL' flag other flags are just ignored](https://developer.android.com/reference/android/support/design/widget/AppBarLayout.LayoutParams.html#SCROLL_FLAG_SCROLL) – TrueCurry Jun 13 '16 at 18:50
  • Tried this solution. It works when there are less number of view that the scrolling height but then doesn't scroll at all if the item are move. This is probably when the items are added after the layout pass. – Tejas Sherdiwala Jun 03 '21 at 05:41
0

I think creating a fixed header and footer could solver your problem. I would've wrote this in the comments but I don't have 50 rep. You could figure out how to do it here

Anthony
  • 1,439
  • 1
  • 19
  • 36
  • Thanks, there was a similar answer here that was removed a few days ago (not by me) - the solution you are referring to doesn't use the new support library – Noa Drach Jun 19 '15 at 22:06
0

I did something along the lines of ensuring I added android:layout_gravity="end|bottom" to the layout in XML that I wanted at the bottom of the CoordinatorLayout

and then set in code:

 mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @SuppressLint("NewApi")
        @SuppressWarnings("deprecation")
        @Override
        public void onGlobalLayout() {
            if (mFooterView != null) {
                final int height = mFooterView.getHeight();
                mRecyclerView.setPadding(0, 0, 0, height);
                mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        }
    });

Note: that the footer View/ViewGroup needs to be higher in the z-axis (listed below the RecyclerView in XML) to function properly

AllDayAmazing
  • 2,383
  • 1
  • 24
  • 25
0

Surround your elements with a linearlayout, like that:

<android.support.design.widget.CoordinatorLayout >

  <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout>
      <android.support.v7.widget.Toolbar />
    </android.support.design.widget.AppBarLayout>
    <include layout="@layout/content_main" />

    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>
  • 2
    Do NOT try this answer, the LinearLayout will prevent your toolbar from collapsing on scroll. As explained in the official docs (https://developer.android.com/reference/com/google/android/material/appbar/AppBarLayout) The AppBarLayout MUST be a direct child of a CoordinatorLayout or it won't work. – Ramiro G.M. Mar 13 '21 at 16:38
0

Android CoordinatorLayout Bottom Layout Behaviour Example

activity_bottom.xml

<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.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimaryDark"
            app:layout_scrollFlags="scroll|enterAlways"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#C0C0C0"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <com.example.android.coordinatedeffort.widget.FooterBarLayout
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_gravity="bottom">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="#007432"
            android:gravity="center"
            android:text="Footer View"
            android:textColor="@android:color/white"
            android:textSize="25sp" />
    </com.example.android.coordinatedeffort.widget.FooterBarLayout>

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

FooterBarLayout.java

FooterBarBehavior.java

Ahamadullah Saikat
  • 4,437
  • 42
  • 39
-2

There is a library for your problem. Hope this will really help for you Here is the library

And another problem you have mentioned fixed the footer. the below one is the relative layout so use the feature android:layout_alignParentBottom="true" on your footer.

Hope you i have solved the issue

nifCody
  • 2,394
  • 3
  • 34
  • 54