Where to put floating action button when there are multiple fragments - android

I'm trying to create a layout with a MainActivity which incorporates a BottomNavigationBar and a FrameLayout for swapping out fragments. I want the BottomNavigationBar to stay fixed with every fragment having a fab which on the other hand disappears when scrolling. The problem is that I'm not sure which layout to use for the MainActivity and the Fragments and where to put the fab, I could put the fab either in the MainActivity's layout and change the behaviour of the fab for each fragments (don't know how though) or add a fab to every fragment's layout. When I do the latter I cannot reference the MainActivity's bottom bar to stay over it.
I tried #1:
MainActivity:
<CoordinatorLayout>
<FrameLayout/>
<BottomNav/>
<FAB/>
</CoordinatorLayout>
And #2:
<CoordinatorLayout>
<FrameLayout/> -> fabs defined in each fragment layout
<BottomNav/>
</CoordinatorLayout>
My Main activites xml file looks somewhat like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/bottom_navigation"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/bottom_navigation"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And one of my fragments with FAB:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
tools:context=".View.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/note_item"
android:layout_gravity="top" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/button_add_note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginBottom="64dp"
android:layout_marginEnd="16dp"
android:src="#drawable/ic_add"
app:layout_anchor="#id/recycler_view"
app:layout_anchorGravity="bottom|right|end"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I want the bottom nav and coordinator layout to be aware of the fab and the fab to be above the bottom nav without using hard-coded dp values but how do I do that?

If you want to use CoordinatorLayout.Behavior logic, your fab should at the same level with CoorditorLayout.
For you i recommend this logic: Activity contains BottomNavigation, FrameLayout (fragment container). Then all your fragment contains:
<CoordinatorLayout>
<RecyclerView/>
<FloatingButton/>
</CoordinatorLayout>
It will give a power to construct UI of fragment exactly how you want.

Related

Why does my RecyclerView not scroll when placed inside an AppBarLayout?

I have an AppBarLayout in my app along with a FrameLayout that I use as a placeholder to load in fragments:
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="#style/AppTheme.AppBarOverlay">
<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/AppTheme.PopupOverlay"/>
<FrameLayout android:id="#+id/main_content_fragment"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="#color/white" />
</android.support.design.widget.AppBarLayout>
The fragment in question looks like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar android:id="#+id/loading_downloaded"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:id = "#+id/items_downloaded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent"
/>
</LinearLayout>
The Fragment is substituted in from code as you might anticipate:
val fragMan: FragmentManager = getSupportFragmentManager()
fragMan.beginTransaction().add(R.id.main_content_fragment, fragment).commit()
The problem is that I cannot scroll my RecyclerView when I format it this way. If I move the FrameLayout outside of the AppBarLayout it works perfectly but then the fragment lies behind the app bar, which is very untidy. This confirms for me that the fragment is working correctly, I just can't figure out why the fragment's scroll behaviour changes when it's within the AppBarLayout.
What do I need to do to be able to scroll my content? Or have I misunderstood and I need to display my fragments outside the AppBarLayout where they scroll correctly and shift everything down by the height of the app bar?
Your fragment's content is meant to be placed as a sibling of the AppBarLayout. You should wrap everything inside a CoordinatorLayout and set this attribute in your FrameLayout where your fragment resides: app:layout_behavior="#string/appbar_scrolling_view_behavior".
Here's your XML structure would be:
<CoordinatorLayout>
<FrameLayout
app:layout_behavior="#string/appbar_scrolling_view_behavior" ... >
...
</FrameLayout>
<AppBarLayout> ... </AppBarLayout>
</CoordinatorLayout>
Having this setup would allow your fragment to show all of it's views with your AppBarLayout. CoordinatorLayout will take care of everything for you.
Here's the official documentation:
AppBarLayout also requires a separate scrolling sibling in order to
know when to scroll. The binding is done through the
AppBarLayout.ScrollingViewBehavior behavior class, meaning that you
should set your scrolling view's behavior to be an instance of
AppBarLayout.ScrollingViewBehavior. A string resource containing the
full class name is available.
See this link for more details: https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html

Collapse layout in a fragment under toolbar

The following is my layout in a Fragment.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="#+id/parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragments.TAClaimFragment"
android:background="#drawable/main_gradient"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_anchor="#id/parent"
app:layout_anchorGravity="top">
<android.support.v7.widget.SearchView
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="?actionBarSize"
android:layoutDirection="rtl"
android:id="#+id/ta_search"
android:animateLayoutChanges="true"
app:searchIcon="#drawable/ic_search"
app:closeIcon="#drawable/ic_close"
android:tooltipText="tooltip"
app:searchHintIcon="#drawable/ic_search"
app:queryHint="#string/search_hint"
/>
<ImageView
android:layout_width="0dp"
android:id="#+id/ta_menu"
android:padding="16dp"
android:layout_weight="0.3"
android:layout_height="50dp" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/ta_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
The RecyclerView has a SearchView and an ImageView above it in a linear layout I want the linear layout to disappear under the toolbar which the Activity containing the fragment has. I am not familiar enough with coordinator layout or collapsing toolbar layout, however I guess the solution lies in the use of the two.
How do I go about it?
I want the linear layout to disappear under the toolbar which the
Activity containing the fragment has.
If you are showing the Fragment by a FrameLayout (Or whatever), show the Fragment like following layout: (FrameLayout inside NestedScrollView for being the layout scrollable)
<CoordinatorLayout>
<AppBarLayout>
<Toolbar/>
</AppBarLayout>
<NestedScrollView>
<FrameLayout />
</NestedScrollView>
</CoordinatorLayout>
And then your Fragment codes (like your current layout codes) in another layout called myfragmentlayout.xml.
So, here, if you are trying to hide the LinearLayout, it will be disappear when you scroll the RecyclerView. Or, you can create a CollpasingToolbarLayout which contains a picture then if you scroll the content, it will be disappear.
Example for CollapsingToolbarLayout with an ImageView inside. (And so much more on SO by CollapsingToolbarLayout tag!):
How to add an ImageView with the title in collapsingtoolbarlayout in Android

Floating Action Button position

I have a Fragment in the bottom of my layout, and when it is shown it overlaps with my FAB.
So, how can I make FAB to depend on this Fragment (lift FAB when the Fragment is shown, just like it does when Snackbar is shown)?
The behaviour between the FAB and the Snackbar is result of having the CoordinatorLayout as the direct parent of the two views. The coordinator layout handles this animation effect.
Coordinator layout is used as a container for a specific
interaction with one or more child views.
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.
Reference: Coordinator Layout | Android Developers
So your view holding the fragment needs to have a Behaviour implementation for it. This will define how your views interact with other views.
Check this guide for how to create Behaviours for CoordinatorLayout.
For FloatingActionButton this behaviour is already defined in its class. Check source here.
I've found a solution. Here is my layout:
<RelativeLayout
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.CoordinatorLayout
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/mediaPlayerControlBar">
<RelativeLayout>
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
app:layout_behavior="com.unimusic.ScrollAwareFABBehavior"
app:fabSize="normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:src="#drawable/ic_search_white_24dp"
android:id="#+id/fab"
android:layout_margin="16dp"
app:elevation="4dp"/>
</android.support.design.widget.CoordinatorLayout>
<fragment android:name="com.unimusic.mediaPlayerControlBar"
android:layout_width="match_parent"
android:layout_height="70dp"
android:id="#+id/mediaPlayerControlBar"
android:layout_gravity="bottom"
app:layout_anchor="#id/snackbarPosition"
app:layout_anchorGravity="bottom"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
Basicly, I've made Relative layout a root component instead of CoordinatorLayout.

CoordinatorLayout and hiding the toolbar

I'm using a single activity - multiple fragments structure.On the activity i'm using v7 toolbar as action bar . I have sever fragments with viewpagers , some with tabs as well.In one of my fragments i have recyclerview . My goal is when i'm scrolling i wanna collapse the bar and hide the floating action button.My floating button is on inside a CoordinatorLayout so it can be displayed properly.But this is inside the layout of the fragment(which is part of a viewpager) , and the activity_main.xml has root element another CoordinatorLayout. Does the fragment CL intercepts the work of the CL inside the layout of the activity,because when i scroll the recycler nothing happens.
Should i use new toolbar for every fragment or i can use one in the
activity and implement different behavior for into fragments?
Can anyone references any examples of using CoordinatorLayout and
collapsing the bar within nested fragments?
activity_main.xml
<android.support.design.widget.CoordinatorLayout
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:gravity="center"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
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/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
</android.support.design.widget.CoordinatorLayout>
The work of the main CoordinatorLayout wasn't intercepted.Adding app:layout_behavior to the frame layout which is fragment container did the job.
Looks like this now:
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".MainActivity" />

CoordinatorLayout overlapping

Have a look at the following layout. You will see, that the floating button is too far to the bottom. This is because the Toolbar and the Tabs are shown and the height of the ViewPager is wrong. So somehow I'm doing something wrong with the layout_height. But how can I fix that?
Remark: The ViewPager is the main content and it contains a fragment with a ListView and a Google Map V2 in the second tab.
That's the layout XML:
<?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: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:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/pager_list_views"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="fill_parent">
</android.support.v4.view.ViewPager>
</android.support.design.widget.CoordinatorLayout>
Here's the layout for the fragment in the first tab (the list):
<?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">
<ListView
android:id="#+id/preview_list"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:orientation="vertical" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/action_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="#mipmap/ic_add_white_48dp" />
</android.support.design.widget.CoordinatorLayout>
Just to make sure; it's not a problem with the FAB. See this picture. It's a similar layout. A CoordinatorLayout with a ToolBar and a ViewPager which swipes through all detail-entries (therefore no tabs are needed). And again, the inner view seems to be too long (the same height as the ToolBar).
You should use android.support.v7.widget.RecyclerView and not ListView
Although you are already using android.support.v7.widget.RecyclerView be 100% sure that you have declard compile 'com.android.support:recyclerview-v7:23.0.0' in your build.gradle dependencies. I encountered the same issue where the viewpager overlaps the system buttons. I fixed it by simply adding this dependency.
Anything you what to be "coordinate" need to be direct child of CoordinatorLayout, Including the AppBar, RecyclerView (ListView in API21+ or other view support nested scroll is OK), or FAB, etc.
The reason why your FAB is offset out of screen, is that:
ViewPager has a #string/appbar_scrolling_view_behavior, the implement of this behavior will offset view when you scroll.
you put FAB inside ViewPager.
So when the offset of ViewPager changed, anything inside ViewPager will offset together (extra CoordinatorLayout has no help to change offset).
To fix this, don't use CoordinatorLayout outside ViewPager.
Or:
Put your FAB out of ViewPager so it won't scroll with ViewPager.
If the FAB only work with some of your page, hide() it when need.
BTW, there is very good App, Cheesesquare Sample, to demo the design library.

Categories

Resources