Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - footer scrolls off screen when used in CoordinatorLayout

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

like image 591
Noa Drach Avatar asked Jun 11 '15 10:06

Noa Drach


People also ask

How do I use CoordinatorLayout?

By specifying Behaviors for child views of a CoordinatorLayout you can provide many different interactions within a single parent and those views can also interact with one another. View classes can specify a default behavior when used as a child of a CoordinatorLayout by implementing the AttachedBehavior interface.

What is Android CoordinatorLayout?

Android CoordinatorLayout is a super-powered FrameLayout. It has a lot more to offer than it seems. It has additional level of control over it's child views. It coordinates the animations and transitions of child views with one another.

What is layout_collapseMode?

In addition to pinning a view, you can use app:layout_collapseMode="parallax" (and optionally app:layout_collapseParallaxMultiplier="0.7" to set the parallax multiplier) to implement parallax scrolling (say of a sibling ImageView within the CollapsingToolbarLayout ).


2 Answers

I use a simplified version of Learn OpenGL ES's solution (https://stackoverflow.com/a/33396965/778951) -- which improves on Noa's solution (https://stackoverflow.com/a/31140112/1317564). 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();     } } 
like image 173
jhavatar Avatar answered Sep 21 '22 07:09

jhavatar


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;     } } 
like image 27
Noa Drach Avatar answered Sep 23 '22 07:09

Noa Drach