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
after scroll
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
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.
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.
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 ).
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(); } }
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; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With