I have a bottom sheet with its height and width set to match_parent. So when on button click I set the behavior to STATE_EXPANDED like this:
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
My Bottomsheet is defined as below:
<FrameLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:elevation="#dimen/design_appbar_elevation"
app:behavior_hideable="true"
app:layout_behavior="#string/bottom_sheet_behavior">
<include
android:id="#+id/bottom_sheet_content"
layout="#layout/bottomsheet_layout" />
</FrameLayout>
I am monitoring states with the BottomSheet Callbacks.
I click on a button and bottom sheet expanded to full screen.
Its current State is STATE_EXPANDED
I quickly swipe down on the bottom sheet. (Not fully drag till it closed, simple swipe down like scrolling)
It stops at the middle and its state is logged as STATE_COLLAPSED
If I swipe again it is all gone and its state is STATE_HIDDEN
I don't understand why it stops in the middle. How can I make it hidden with a single swipe.
I tried that by setting peek_height to 0dp. By this, it never encounters the STATE_HIDDEN. When hidden, its state becomes STATE_COLLAPSED. I just don't understand this states.
How to achieve STATE_HIDDEN with a single swipe down?
Kinda late but I just stumbled upon this while searching for something similar.
This is how you can skip the collapsed state:
In XML by adding app:behavior_skipCollapsed="true" to the BottomSheet view.
OR
Programmatically with setSkipCollapsed(boolean).
Related
I have a rather complex BottomSheetLayout which layout is as follow
The root view of my bottom sheet is a custom FrameLayout that allows to round it's corner (both background and children). Nothing else (nothing touch-related)
Then, I use the usual ConstraintLayout in order to layout my Bottom sheet.
This ConstraintLayout contains, amongst other views, a vertical RecyclerView:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp">
<!-- other views -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/events"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="25dp"
android:layout_marginBottom="74dp"
app:layout_constraintTop_toBottomOf="#+id/days"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#{viewModel.colors.defaultBackgroundColor}"
tools:background="#ECF0F3"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="#layout/event_item"
tools:itemCount="10" />
</androidx.constraintlayout.widget.ConstraintLayout>
I have no particular issue while dragging my bottom sheet, however, when fully expanded I was expecting the be able to scroll the content of my RecyclerView. But I cannot.
After a lot of researches, I managed to make it scroll by enabling scrolling when my Fragment's view is inflated :
ViewCompat.setNestedScrollingEnabled(this.binding.bottomSheetEvents.getRoot(), true);
However, doing so has a weird consequence. When my bottom sheet's state is EXPANDED, I can finally scroll my RecyclerView, but then there is absolutely no way to drag my Bottom sheet any more : it remains fully expanded.
I have tried a few other ways.
I have tried wrapping my NestedScrollView. In past experience I was able to have the full content of my bottom sheet scrollable thanks to NestedScrollView, but in this case, I only want to scroll my RecyclerView. What ever is above it must remain idle.
I have tired this.binding.bottomSheetEvents.events.setNestedScrollingEnabled(false); but there is no difference.
My belief is that when the bottom sheet is fully expanded, it dispatches scroll events to inner children that can supports its. And, backwards, it knows, at some point, when uses wishes to collapse said bottom sheet. So I guess, something wrong must be happening there.
Further informations:
this bottomsheet is included in my fragment which roots view is a CoordinatorLayout obviously.
the fragment is also hosted in CoordinatorLayout with an AppBar
the include layout uses the app:layout_behavior="#string/bottom_sheet_behavior"
and the include layout also uses behavior_fitToContents set to false so that I can use method setExpandedOffset to prevent the bottom sheet to reach the top.
Version used : 1.1.0-alpha07
Thanks for the help!
I'm struggled with this for hours looking for solution on google and stackoverflow. Thought that this is some trivial bug in my app but finally made empty project and can reproduce this too. Just run new project and select "Tabbed Activity" with navigation style "Action Bar Tabs (with ViewPager)
Then try to put any widget at the bottom of the fragment's layout. I did this by modify fragment_main.xml and adding:
android:layout_height="match_parent"
android:gravity="bottom"
android:textAlignment="center"
So the whole layout is:
<?xml version="1.0" encoding="utf-8"?>
<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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity$PlaceholderFragment">
<TextView
android:id="#+id/section_label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:gravity="bottom"
android:text="aaaaaaa"
android:textAlignment="center"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#+id/constraintLayout"
tools:layout_constraintLeft_creator="1"
tools:layout_constraintTop_creator="1" />
</android.support.constraint.ConstraintLayout>
In design mode everything looks fine:
But when you run app:
You will see text only when you swipe action bar to hide it:
So it is impossible to set widget at the bottom of the tab's fragment or even match some list/picture to the height of the parent because bottom edge will be always under navigation bar.
Workaround which I found is here:
ViewPager with Toolbar and TabLayout has wrong height
First one is to put AppBarLayout and ViewPager between LinearLayout but then I lose hidding action bar functionality when scrolling ViewPager's content. Second one is add android:paddingBottom="?attr/actionBarSize" in ViewPager but then there is a gap when I hide action bar. Seriously there is no solution for this?
I think this is an expected behavior since the ActionBar gets hidden when scrolling up. In the design mode the text can be shown because it doesn't display the TabLayout. However, when you launch the app, it will inflate the TabLayout and the fragment will go below it. So it's not like the fragment is getting expanded or giving you wrong height.
Imagine putting an ImageView that has a matching height of the visible field (from below the TabLayout to right above the navigation menu). When you hide action bar from there, it will have a gap on the bottom since there's no content to fill up the space of hidden action bar, unless you stretch the ImageView as you scroll up, which will result in wired stretched image :/
One possible solution I can think of is, if you want to add a view on the bottom of the fragment, I will set the actionbar padding to the view and when I scroll the screen, I will adjust the padding depends on the scroll offset so that I can always be on the bottom.
I am working with bottom sheet and it is working great if I dont add margin top.
I want the bottom sheet to fill the screen when pulled up but it also goes behind the Action Bar.
To solve this, I added margin top to the bottom sheet equivalent to the height of action bar, it works great until bottom bar is back to bottom.
<RelativeLayout
android:id="#+id/rl_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="280dp"
android:margin_top="60dp"
app:behavior_peekHeight="80dp"
app:behavior_hideable="false"
app:layout_behavior="#string/bottom_sheet_behavior"
android:background="#drawable/ic_camera">
When bottom sheet is slided down, margin is no more there and view above the screen becomes equal to peekHeight + marginTop
Before Expanded
Collapsed after expanding once
One option is to use a Toolbar instead of adding a margin to the BottomSheet. You can put this inside the CoordinatorLayout at the top so it will be behind the BottomSheet. Make sure the View that has the BottomSheetBehavior is at the bottom of the layout and that it has a higher elevation than the Toolbar.
I can suggest you to change the root layout - from CoordinatorLayout to any other (Relative/Linear and so on). And after that you can add your CoordinatorLayout in your new root ViewGroup with desired marginTop.
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="80dp"
I have a RecyclerView using a LinearLayoutManager with HORIZONTAL orientation, nested inside a FrameLayout using the BottomSheet Behavior.
When attempting to drag vertically across the RecyclerView, the BottomSheet doesn't respond to the drag event. Presumably this is because vertical scrolling is disabled for a LayoutManager with horizontal orientation.
I've tried overriding LinearLayoutManager.canScrollVertically() and returning true. This sort of works.. If you drag vertically in a very careful manner, the BottomSheet will respond. As soon as any horizontal movement is involved however, the BottomSheet stops responding to vertical drag events.
I'm not sure if overriding canScrollVertically() is the right approach here - it certainly doesn't feel right from a UX point of view.
I've also noticed that if I use a ViewPager rather than a RecyclerView with a horizontally oriented LayoutManager, the BottomSheet responds to vertical swipe events as desired.
Is there some other method of LayoutManager, RecyclerView, BottomSheet Behavior, or something else altogether that can help propagate the vertical scroll events on to the BottomSheet Behavior?
There's an example of the problem here:
https://github.com/timusus/bottomsheet-test
(Problem can be reproduced with commit #f59a7031)
Just expand the first bottom sheet.
Where does the problem lies? In FrameLayout. BottomSheet works perfectly when put inside CoordinatorLayout. Then BottomSheet can pass it's scrolling state through CoordinatorLayout to other views put as direct children of CoordinatorLayout.
Why RecyclerView was not able to pass scroll state to BottomSheet? It is not a direct child of CoordinatorLayout. But there exists a way to pass them: RecyclerView must be in put in view that implements NestedScrollingParent and NestedScrollingChild. The answer to that is: NestedScrollView
So your fragment_sheetX.xml layouts should look like:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:orientation="vertical"
android:fillViewport="true">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.NestedScrollView>
Notice also android:fillViewport="true" as otherwise, your RecyclerView will not take whole height.
However it still will not work. Why? RecyclerView must be told to pass vertical scrolling to parent. How? The answer is recyclerView.setNestedScrollingEnabled(false);, but that is better described here.
Btw: MultiSheetView is a great feature and a very interesting approach to mobile UX design.
I have the following layout in an activity.
<CoordinatorLayout ...>
<ScrollView ...>
<LinearLayout
...
android:orientation="vertical">
<!-- Some stuff -->
<EditText ...>
<!-- Some more stuff -->
<View
android:id="#+id/keyboard_extender"
...>
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="#+id/bottom_sheet"
android:orientation="vertical"
...
app:layout_behavior="android...BottomSheetBehavior">
<!-- Even more stuff -->
</LinearLayout>
</CoordinatorLayout>
and I want that the keyboard shouldn't push up the bottom_sheet but it should push up the keyboard_extender. Please note that the bottom_sheet is not a BottomSheetDialog but just a view using the android.support.design.widget.BottomSheetBehavior. This can't be changed to a BottomSheetDialog because I want the rest of the screen to remain active at the same time as the bottom_sheet
I never set the visibility of the bottom_sheet to View.GONE. I only change its state from BottomSheetBehavior.EXPANDED to BottomSheetBehavior.COLLAPSED and back. Also, I want the bottom_sheet to be visible only when the keyboard isn't.
Since I need that the keyboard_extender should always be above the keyboard, I have set in the AndroidManifest.xml that android:windowSoftInputMode="adjustResize" (which I assume is unavoidable)
I have a method which when called should hide the keyboard and show the bottom_sheet, but the problem I'm facing is that the bottom_sheet starts animating to BottomSheetBehavior.EXPANDED at the same time that the keyboard starts hiding, and when the keyboard starts hiding, the screen is actually resized because of the windowSoftInputMode="adjustResize" and the bottom_sheet starts expanding from the top of the keyboard and it doesn't move down along with the keyboard. So, when the keyboard completely hides, the bottom_sheet is actually sitting in the middle of the screen (just above the height of the keyboard).
Ideally, I'd like that the keyboard starts hiding and the bottom_sheet starts expanding at the same time, but the bottom_sheet starts expanding from the bottom of the screen and not the top of the keyboard.
The very hacky fix I'm currently using to overcome this issue is to delay the expanding of the bottom_sheet by SoftInput.KEYBOARD_POPUP_DELAY, which seems to be working for the time being. Is there any cleaner alternative?