I have an activity with a coordinator layout. Inside the coordinator layout is a view which inherits the default bottom sheet layout behavior for the Google Support Library Bottom Sheet. The issue is that when I call Snackbar.show() with the coordinator layout as the view, the bottomsheet pops up as well.
Here is the call to show snackbar:
Snackbar.make(coordinatorLayout, R.string.status_image_saved,
Snackbar.LENGTH_SHORT).show();
Here is the layout:
<android.support.design.widget.CoordinatorLayout>
<android.support.constraint.ConstraintLayout
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>
<android.support.v7.widget.RecyclerView />
</android.support.constraint.ConstraintLayout>
<LinearLayout
android:id="#+id/attachment_selector"
android:layout_width="match_parent"
android:layout_height="480dp"
android:background="#color/colorBack"
android:elevation="10dp"
android:orientation="vertical"
app:behavior_hideable="true"
app:behavior_peekHeight="#dimen/bottom_sheet_start"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<android.support.design.widget.TabLayout
android:id="#+id/attachment_selector_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="fixed" />
<android.support.v4.view.ViewPager
android:id="#+id/attachment_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
It's a late answer but might be helpful for others.
If you are using a Dialog or BottomSheetDialog you can show a Snackbar providing the top-level window decor view.
Here's my solution in Kotlin:
Snackbar.make(
dialog.window.decorView, // important part
"your-string",
Snackbar.LENGTH_SHORT
).show()
The solution using dialog.window.decorView appeared over Android's soft navigation keys for me. Instead I shifted the Snackbar's elevation to be greater than the BottomSheetDialog's elevation.
This keeps the Snackbar at the correct position in the y-axis and the new elevation brings it above the bottom sheet:
Snackbar.make(binding.root, "Example", Snackbar.LENGTH_LONG)
.apply {
view.elevation = 1000F
}.show()
Snackbar.make(
dialog.window.decorView,
"your-string",
Snackbar.LENGTH_SHORT
).show()
also add anchor view to SnackBar (you can add view at bottom of the root view in your layout and use it as anchor view in SnackBar)
e.g.,
snackbar.setAnchorView(your_view_here)
Related
The problem is, after expanding the bottom sheet, the area underneath the sheet where the MaterialToolbar is located can't be used to drag the sheet back down. The rest of the sheet can still be used for dragging.
Complete runnable project is at https://github.com/svenoaks/DragBottomSheet
I used the standard Material3 Android Studio project and modified it like this:
activity_main.xml
<?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"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
app:layout_collapseMode="none"
android:layout_height="128dp" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="#+id/standard_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorSecondary"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_hideable="false"
app:behavior_peekHeight="?actionBarSize">
<include layout="#layout/content_main" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Cause:
This behavior has nothing to do with Material3, replacing the MaterialToolbar with something else like Toolbar or even a LinearLayout you'd get the same behavior.
The dragging behavior of the Bottom sheet is blocked because of the FirstFragement's NestedScrollView nested scrolling behavior which catches the drag event instead of the bottom sheet.
Two ways to solve this:
Replace the NestedScrollView with the normal ScrollView which
doesn't have the nested scrolling behavior by default.
Programmatically disable the nested scrolling of the NestedScrollView:
<androidx.core.widget.NestedScrollView
....
android:id="#+id/nested_scroll_view">
class FirstFragment : Fragment() {
// ....
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.nestedScrollView.isNestedScrollingEnabled = false
}
}
Don't set that in layout to avoid InflateException
Side note: you'd add some android:elevation to the bottom sheet to avoid obscuring it by the toolbar.
I'm setting up the app navigation with the new Jetpack Navigation Component and the NavigationUI class to support navigation within a CollapsingToolbarLayout.
However it does not work using the code/method from the documentation.
I've setted up the MainActivity as shown in the documentation:
LinearLayout as Parent View, within an AppBarLayout, within a CollapsingToolbarLayout, and the toolbar itself. And the host fragment.
The navigation start destination is a FrameLayout, which contains the actual RecyclerView.
I tried various layout_scrollFlags and tried to set a CoordinatorLayout as parent view instead of a LinearLayout.
activity_main.xml
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="top"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph"/>
</LinearLayout>
In the MainActivity onCreate function:
// Set Toolbar as ActionBar
setSupportActionBar(findViewById(R.id.toolbar))
val layout = findViewById<CollapsingToolbarLayout>(R.id.collapsing_toolbar_layout)
val toolbar = findViewById<Toolbar>(R.id.toolbar)
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(navController.graph)
layout.setupWithNavController(toolbar, navController, appBarConfiguration)
The first destination where I expect the collapsing behaviour:
<FrameLayout 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" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".restaurant.RestaurantsFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/restaurant_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/restaurant_item"/>
</FrameLayout>
I expect the toolbar collapsing while scrolling through the RecyclerView.
Actual it's just static.
I think there might be a connection between the RecyclerView and the CollapsingToolbar missing?
What am I missing?
Thank you, you are my saviour. I've also struggled with this recently, so I've played around with your solution a bit more, and I've come to a few
conclusions (time of writing 2019-08-08):
TL;DR CollapsingToolbarLayout works too, but fragment is sized incorrectly.
The top level layout must be a CoordinatorLayout, because if it wasn't nothing could coordinate the AppBarLayout and the fragment.
The <fragment> element must have the attribute app:layout_behavior="#string/appbar_scrolling_view_behavior". It seems that CoordinatorLayout only searches among its direct children to find a behavior.
It's actually fine to have a AppBarLayout/CollapsingToolbarLayout/Toolbar hierarchy. As long as the <fragment> has app:layout_behavior attribute, this will
also work fine.
The layout inside the fragment doesn't really matter. In my case use a viewmodel which means I need an extra <layout> wrapper so I've tried both
<layout>
<data></data>
<FrameLayout>
<RecyclerView/>
<FrameLayout>
</layout>
and
<layout>
<data></data>
<CoordinatorLayout>
<RecyclerView/>
<CoordinatorLayout>
</layout>
which both work fine. The inner CoordinatorLayout doesn't swallow the behavior because the RecyclerView doesn't have or use a behavior, but this leads me to my next point:
This arrangement doesn't resize the fragment properly. If you also add a FloatingActionButton to the fragment, it doesn't work with FrameLayout properly (FAB in upper left corner despite app:layout_anchorGravity="bottom|right|end").
If you use a CoordinatorLayout inside the fragment, like in my second example above, the FAB jumps to the right position. Showing and hiding doesn't work, even if you implement yourself a ScrollAwareFABBehavior. Instead what happens is that the FAB scrolls offscreen, showing that the fragment is 100% the vertical height of the viewport, but gets shifted down by the height of the toolbar (if it's an expanded CollapsingToolbarLayout, this could be 300dp or almost half the screen in my case).
I haven't been able to find a way around this problem and I'm going to stop trying for the time being. It seems that the single-activity/single-toolbar model isn't able to handle CoordinatorLayout things just yet. The reason the Android docs use a LinearLayout is probably to make sure the fragment has the correct vertical size, but the give up any dynamic behavior features along the way. I will probably be using nested navigation graphs going forward where each section of the app will have the appropriate toolbar contained within it.
If anyone has any suggestions or pointers, please do comment. I'd like to actually get this working some day.
Found a solution.
Use CoordinatorLayout as Parent View instead of LinearLayout.
Remove the CollapsingToolbarLayout.
Declare layout_scrollFlags in the Toolbar (eg. app:layout_scrollFlags="scroll|enterAlways|snap").
And in the fragment View add app:layout_behavior="#string/appbar_scrolling_view_behavior" (important since we use a RecyclerView in the home Fragment)
main_activity.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways|snap"
/>
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
MainActivity onCreate
// Set Toolbar as ActionBar
setSupportActionBar(findViewById(R.id.toolbar))
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(navController.graph)
findViewById<Toolbar>(R.id.toolbar)
.setupWithNavController(navController, appBarConfiguration)
I'm trying to use a CoordinatorLayout with a BottomNavigationView, an AppBarLayout, and a ViewPager. Here is my 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="enterAlways|scroll"
app:popupTheme="#style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
app:itemIconTint="?colorPrimaryDark"
app:itemTextColor="?colorPrimaryDark"
app:menu="#menu/navigation"/>
</android.support.design.widget.CoordinatorLayout>
The problem is that the CoordinatorLayout places the ViewPager to extend to the bottom of the screen, so the bottom is obscured by the BottomNavigationView, like this:
This happens even though the CoordinatorLayout itself doesn't extend down so far:
I've tried adding app:layout_insetEdge="bottom" to the BottomNavigationView and app:layout_dodgeInsetEdges="bottom" to the ViewPager, but that has a different problem: it shifts the bottom of the ViewPager up, but it keeps the same height, so the top is now chopped off:
I tried two other experiments. First, I tried removing the BottomNavigationView from the CoordinatorLayout and making them siblings under a vertical LinearLayout. Second, I put the ViewPager and BottomNavigationView together under a LinearLayout, hoping they would layout out correctly. Neither helped: in the first case, the CoordinatorLayout still sized the ViewPager with respect to the entire screen, either hiding part of it behind the BottomNavigationView or chopping off the top. In the second case, the user needs to scroll to see the BottomNavigationView.
How do I get the layout right?
P.S. When I tried the layout suggested by #Anoop S S (putting the CoordinatorLayout and the BottomNavigationView as siblings under a RelativeLayout), I get the following (with the ViewPager still extending down behind the BottomNavigationView):
As before, the CoordinatorView itself only extends down to the top of the BottomNavigationView.
I came up with a different approach (not battle tested yet though):
I subclassed AppBarLayout.ScrollingViewBehavior to adjust the bottom margin of the content view based on the height of the BottomNavigationView (if present). This way it should be future proof (hopefully) if the height of the BottomNavigationView changes for any reason.
The subclass (Kotlin):
class ScrollingViewWithBottomNavigationBehavior(context: Context, attrs: AttributeSet) : AppBarLayout.ScrollingViewBehavior(context, attrs) {
// We add a bottom margin to avoid the bottom navigation bar
private var bottomMargin = 0
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
return super.layoutDependsOn(parent, child, dependency) || dependency is BottomNavigationView
}
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
val result = super.onDependentViewChanged(parent, child, dependency)
if(dependency is BottomNavigationView && dependency.height != bottomMargin) {
bottomMargin = dependency.height
val layout = child.layoutParams as CoordinatorLayout.LayoutParams
layout.bottomMargin = bottomMargin
child.requestLayout()
return true
} else {
return result
}
}
}
And then in the layout XML you put:
app:layout_behavior=".ScrollingViewWithBottomNavigationBehavior"
instead of
app:layout_behavior="#string/appbar_scrolling_view_behavior"
Basically what you have to do is create a Relativelayout as parent and put BottomNavigationView and CoordinatorLayout as children. Then align BottomNavigationView at the bottom and set CoordinatorLayout above that. Please try the below code. It might have few attribute erros, because I wrote it here itself. And sorry for the messed up indentation.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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=".MainActivity">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/navigation"
>
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="enterAlways|scroll"
app:popupTheme="#style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?android:attr/windowBackground"
app:itemIconTint="?colorPrimaryDark"
app:itemTextColor="?colorPrimaryDark"
app:menu="#menu/navigation"/>
</RelativeLayout>
This is caused by app:layout_behavior="#string/appbar_scrolling_view_behavior" in your ViewPager. If you remove this line, you will see now it fits the CoordinatorLayout container (unfortunately, this includes now being underneath the Toolbar).
I found it helped to treat CoordinatorLayout as just a FrameLayout, with a few extra tricks. The app:layout_behavior attribute above is necessary to allow the toolbar to appear to scroll in and out... in reality, the layout is doing this by having the view linked to the collapsing toolbar (in your case, your ViewPager) be exactly a toolbar's height larger than the bounds. Scrolling up brings the view up to the bottom within the bounds, and pushes the toolbar up extending beyond the bounds. Scrolling down, vice versa.
Now, onto the BottomNavigationView! If, as I did, you want the BottomNavigationView visible the whole time, then move it outside the CoordinatorLayout, as Anoop said. Use CoordinatorLayout only for things that need to coordinate, everything else outside. I happened to use a ConstraintLayout for my parent view (you could use RelativeLayout or whatever works for you though). With ConstraintLayout, for you it would look like this:
<android.support.constraint.ConstraintLayout
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:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="enterAlways|scroll"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:itemIconTint="?colorPrimaryDark"
app:itemTextColor="?colorPrimaryDark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/navigation" />
</android.support.constraint.ConstraintLayout>
In Android Studio design view, you're still going to see the ViewPager appear to be larger than the container (probably looks like it's behind the Bottom Nav still). But that's ok, when you get to the bottom of the ViewPager's content, it will show (i.e. won't be behind the bottom navigation). This quirk in the design view is just the way the CoordinatorLayout makes the toolbar show/hide, as mentioned earlier.
I had a similar problem with a layout very close to OP's and a ViewPager with 3 pages but only page 2 and 3 which should be affected by appbar_scrolling_view_behavior.
After struggling for hours exploring dead-end possible solutions (layout_dodgeInsetEdges, Window insets, attempting to modify ViewPager's page measured size, android:clipChildren, fitSystemWindows, ...), I finally found an easy solution detailed below.
As Vin Norman explained, ViewPager overlapping BottomNavigation is entirely caused by appbar_scrolling_view_behavior set on the ViewPager. AppBarLayout will just make fullscreen the sibling that has appbar_scrolling_view_behavior. That's how it works.
If you only need this behavior on certain ViewPager pages, there is a simple fix than you can apply on the ViewPager's OnPageChangeListener to dynamically change the Behavior and add/remove required padding:
public class MyOnPageChangeListener extends ViewPager.SimpleOnPageChangeListener {
#Override
public void onPageSelected(int position) {
...
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) _viewPager.getLayoutParams();
if(position == 0) {
params.setBehavior(null);
params.setMargins(params.leftMargin, _appBarLayoutViewPagerMarginTopPx,
params.rightMargin, _appBarLayoutViewPagerMarginBottomPx);
} else {
params.setBehavior(_appBarLayoutViewPagerBehavior);
params.setMargins(params.leftMargin, 0, params.rightMargin, 0);
}
_viewPager.requestLayout();
}
}
For page at position 0 (the one we want the ViewPager to extend exactly below the Toolbar and above the BottomNavigationView), it removes the behavior and adds top and bottom padding, respectively _appBarLayoutViewPagerMarginTopPx and _appBarLayoutViewPagerMarginBottomPx that are constants easy to compute beforehand (respectively the value in pixel for R.attr.actionbarSize and the height for the NavigationBottomView. Usually both are 56dp)
For all other pages needing appbar_scrolling_view_behavior we restore the associated scrolling behavior (stored beforehand in _appBarLayoutViewPagerBehavior) and remove top and bottom padding.
I tested this solution and it works fine without caveat.
In case anyone is still searching for a solution of this problem:
Cause of the problem is that CoordinatorLayout is not calculating correctly size of AppBarLayout because it has Toolbar with app:layout_scrollFlags="enterAlways|scroll" setting. It thinks that Toolbar will hide when scrolling so it leaves all available space to ViewPager, but actually what happens is that toolbar shows so ViewPager moves down, behind NavigationBar.
Easiest way to solve this is just to add android:minHeight="?attr/actionBarSize" (or whatever toolbar height you are using) to AppBarLayout. This way CoordinatorLayout will know properly how much space it needs to leave for ViewPager.
If it still matters to someone:
In the answer of Anoop SS above, trying replacing the RelativeLayout with LinearLayout. Also set layout_height of CoordinatorLayout to 0dp and set layout_weight to 1.
I had almost the same problem....just that i wanted to have a static AdView at the bottom instead of the BottomNavigationView. Trying Anoop SS suggestions, at first, I got the same behaviour as OP: ViewPager extended behind the AdView. But then I did what I suggested about and everything worked fine.
Android layouts behave in weird manner or may be it is the lack of good documentation or the lack of knowledge on our part....but making a layout is just too annoying most of the time.
If you are using Androidx try this
<RelativeLayout 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:fitsSystemWindows="true"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:layout_above="#+id/bottomNavView">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.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"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/mobile_navigation" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?android:attr/windowBackground"
app:menu="#menu/bottom_nav" />
I have an app layout with a custom toolbar and a persistent BottomSheet - both inside of a CoordinatorLayout.
On a button click I want to show the BottomSheet. Right now the sheet is displayed fullscreen and overlays the toolbar. By setting the app theme to Theme.AppCompat.Light.DarkActionBar the BottomSheet stays below the ActionBar, but the bar cannot be customized.
Is there a way to limit the height of the persitent BottomSheet to fullscreen - ActionBar height?
This is my code in activity_main.xml
<android.support.design.widget.CoordinatorLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:attrs="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="com.test.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
app:elevation="20dp"
android:elevation="20dp"
android:layout_height="?attr/actionBarSize">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
app:elevation="20dp"
android:elevation="20dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
</android.support.design.widget.AppBarLayout>
</LinearLayout>
<include layout="#layout/bottom_sheet_additem"/>
</CoordinatorLayout>
Here is the code of sheet_bottom.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/bottomSheetLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorAccent"
app:behavior_hideable="true"
app:behavior_peekHeight="0dp"
android:fitsSystemWindows="true"
app:layout_behavior="#string/bottom_sheet_behavior">
<TextView
android:id="#+id/bottomsheet_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Lorem Ipsum Dolor..."
android:textColor="#FFFFFF" />
</RelativeLayout>
The image on the left hand side shows the BottomSheet which stops below the Toolbar - which is not working with my current code. Currently it looks like the picture on the right.
I had the same problem... I don't know if it's the best solution, but for now, worked for me.
Try to put your include inside another CoordinatorLayout in your activity_main.xml, with a marginTop like this:
<android.support.design.widget.CoordinatorLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:attrs="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="com.test.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
app:elevation="20dp"
android:elevation="20dp"
android:layout_height="?attr/actionBarSize">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
app:elevation="20dp"
android:elevation="20dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
</android.support.design.widget.AppBarLayout>
</LinearLayout>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="56dp">
<include layout="#layout/bottom_sheet_additem"/>
</android.support.design.widget.CoordinatorLayout>
</CoordinatorLayout>
I hope it helps.
We can use app:layout_behavior instead of fixed height
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<include layout="#layout/bottom_sheet_additem"/>
</android.support.design.widget.CoordinatorLayout>
Your sheet_bottom should look like this
<?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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/activity_main">
<RelativeLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:behavior_hideable="true"
app:behavior_peekHeight="?android:attr/actionBarSize"
app:elevation="#dimen/size_5dp"
app:layout_behavior="#string/bottom_sheet_behavior">
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
The accepted answer works when you expand the bottom sheet to the full screen, but when collapsing it, it adds additional margin that makes a part of the collapsing layout hidden below the screen, so I decided to put the margin programmatically by listening to the collapse/hidden status of the BottomSheet.
First add the BottomSheet within a CoordintorLayout in xml
And add below callback listener.
mBottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
CoordinatorLayout bottomSheet = findViewById(..); // inflate the bottom sheet
CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) bottomSheet.getLayoutParams();
if (newState == BottomSheetBehavior.STATE_COLLAPSED)
layoutParams.setMargins(0, 0, 0, 0); // remove top margin
else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
layoutParams.setMargins(0, 100, 0, 0); // add top margin
bottomSheet.setLayoutParams(layoutParams);
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
}
});
+1 for updating the margin programmatically. While the hidden part of the BottomSheet can be offset by increasing the peek height, using just the original solution above means the margin projects onto the underlying UI elements so the scroll action area projects off the actual BottomSheet onto the other UI.
Using the onStateChanged method in the BottomSheetCallback means acting on the event that the BottomSheet has already expanded or collapsed. Adding or removing the margin at this stage can result in seeing 'jerky' behaviour where for example the sheet momentarily reaches the fully expanded state covering the appbar before the margin is then programmatically applied to shift the UI components down resulting in a 'flash' as this applies.
Instead I used the onSlide method to detect when the BottomSheet was being slid up or down and added or removed the margins only once the sheet was half way through the transition. If the margins are applied too early in the slide motion then again the user can see the BottomSheet UI jumping up or down not long after initiating the action (they are less likely to notice this at the half way point if they have done a 'fling' up or down motion.
Also I found it worked best to fetch the height of the AppBar and the status bar and use those to set the required padding value for accurate placement in expanded mode.
This challenge can be avoided altogether by using a widget to trigger the BottomSheet state change programmatically.
#Override
public void onSlide(View bottomSheet, float slideOffset) {
boolean inRangeExpanding = oldOffSet < slideOffset;
boolean inRangeCollapsing = oldOffSet > slideOffset;
oldOffSet = slideOffset;
if (inRangeCollapsing && slideOffset < 0.5f) {
//reset padding on top of bottomsheet so there is no padding/overlap onto underlying sheet (which overlaps underlying sheet and so interfers with scrolling behaviour
bSheetView.setPadding(0,10,0,0);
Log.d(TAG,"onSlide STATE_COLLAPSING");
} else if(inRangeExpanding && slideOffset > 0.5f){
//reset padding on top of bottomsheet so there is padding/overlap onto underlying sheet so it does not write over the top of the menu appbar
bSheetView.setPadding(0, topMargin,0,0);
Log.d(TAG,"onSlide STATE_EXPANDING");
}
}
I need some help figuring out, what's wrong with my Snackbars (Design Support), which are not showing up.
I have a VideoView (FrameLayout containing a dynamically created SurfaceView).
The layout file is:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
android:id="#+id/coordinator"
android:background="#android:color/white"
tools:context="com.denisloh.typhoonCamera.MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<VideoView
android:id="#+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top|start"
android:background="#drawable/toolbar_gradient"
android:theme="#style/AppTheme.AppBarOverlay"
app:popupTheme="#style/AppTheme.PopupOverlay">
<android.support.v7.widget.ActionMenuView
android:id="#+id/action_bar"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
</android.support.v7.widget.Toolbar>
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|end"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_camera_white_24dp" />
</android.support.design.widget.CoordinatorLayout>
I create and show the snackbar with this snippet:
...
mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinator);
...
private void showWifiSettingsSnackBar(final String message) {
Snackbar.make(
mCoordinatorLayout,
getString(R.string.wifi_error_open_settings, message),
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.wifi_settings, new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
}
})
.show();
Log.d(TAG, "Showing snackbar.");
}
When I try to open the snackbar it isn't shown. However, when I remove the VideoView from my layout, it is shown without any problems.
So, I guess, that the VideoView is hiding it or the snackbar is shown out of bounds. To check that, I replaced the VideoView with a basic View of the same size. But there, I can see the snackbars.
Did someone else had a similar issue with SurfaceViews in connection with Snackbars?
try adding, videoView.setZOrderOnTop(false); in your java file.
Attach the snack bar to your VideoView instead of CoordinatorLayout
I have got a similar problem, while using WebView in Fullscreen and AppCompat, the Snackbar is hiden. My solution for the issue:
• Init the snackbar.
View rootView = super.findViewById(R.id.mainContainer);
Snackbar snack = Snackbar.make(rootView, message, Snackbar.LENGTH_INDEFINITE);
snack.setAction("OK", callback);
View view = snack.getView();
• Important for showing the snack foreground.
view.requestFocusFromTouch();
• Set position Top and show the snack. (!Important: Setting it Bottom will hide it overlay.
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
params.setGravity(Gravity.TOP);
view.setLayoutParams(params);
snack.show();