RecyclerView (horizontal) nested in BottomSheet preventing vertical scrolling - android

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.

Related

BottomSheet: issue with nested scrolling (RecyclerView & NestedScrollView)

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!

RecyclerView inside NestedScrollview not scrolling smoothly

When I'm scrolling down, the items above the RecyclerView does not scroll unless I start touching from the layout above, and it only scrolls down when I have reached the end of the RecyclerView.
<NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout>
<Some other items.../>
</LinearLayout>
<RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</NestedScrollView>
Note:
I actually use a fixed size for the RecyclerView, setting it via the code below:
float height_recyclerview = (ScreenUtil.getHeight(context) - (height_banner + height_bottom_navigation + height_create_post));
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) height_recyclerview);
rv.setLayoutParams(layoutParams);
Why do I use fixed size if it works smoothly with wrap_content?
I will be displaying possibly thousands of items that may have
images, which will hurt performance if it does not actually do
recycling because of the issue that the RecyclerView is inside the
NestedScrollView
I have implemented an EndlessRecyclerViewScrollListener which has an
issue that it keeps loading more data from server continuously if
implemented with a RecyclerView that is within whatever scrollable
view, or if it is in a scrollable view, but does not have a fixed
height, even if you are not scrolling down.
I have tried the following:
set nested scrolling to false on the recycler view
try using scroll view instead of nested scroll view
a bunch of other code related to layouts and scrolling behaviors that others suggested which didn't work for me because I'm implementing it in a much more complicated layout and the fact that I use EndlessRecyclerViewScrollListener
What I want to fix?
I want to make the page scroll like a single page, not as a separate scrollable view.
Note that my recycler view has a fixed height that takes the entire screen's space meaning that its height is actually fit assuming that the linear layout above is not visible anymore if the user has scrolled down.
The ideal scenario is to make the scrollview scroll down first, to make the recycler view take the entire screen, so that the recyclerview will scroll however the user wants to.
Then the linearlayout above which should not be visible anymore if the recycler view has taken up all the space of the screen, should only show up if the recycler view has reached the top/first item, if the user keeps scrolling back up.
Read this.
Add app:layout_behavior="#string/appbar_scrolling_view_behavior" to your recycler xml.
<android.support.v7.widget.RecyclerView
android:id="#+id/conversation"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
NestedScrollView Smooth Scrolling
recyclerView.isNestedScrollingEnabled = true
Do this programmatically
<androidx.core.widget.NestedScrollView 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="0dp"
android:fillViewport="true"
...

Android nested scrollview anchored to AppBArLayout

So, I have a situation, I need to create something like this:
Where white background view is nested view, and the image with all the elements is in the appbar. Now, I managed to create it without NestedScrollView being over the AppbarLayout, but how can I achieve that, how can I put NestedScrollView above AppBarLayout without loosing any functionality?
In oreder to make this work, you need to do following:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:behavior_overlapTop="64dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
Where overlapTop is how much it will overlap and layout_behavior is the thing you need here. Hope it helps.

Where should 'app:layout_behavior' be set?

Should it be set at the AppBarLayout sibling's parent or at the first Scrollable View inside its sibling?
With Material Design for Android, there are Views that let us work with the behavior of the layout depending on its surroundings, one of them is the CoordinatorLayout, as this CodePath guide mentions:
CoordinatorLayout extends the ability to accomplish many of the
Google's Material Design scrolling effects. Currently, there are
several ways provided in this framework that allow it to work without
needing to write your own custom animation code.
The one I'm interested in now is:
Expanding or contracting the Toolbar or header space to make room for the main content.
So, we would use the AppBarLayout with a Toolbar with app:layout_scrollFlags set and another ViewGroup sibling to the AppBarLayout with app:layout_behavior.
My question is: in what exact ViewGroup (or maybe View) should we put that
app:layout_behavior?
So far, I've tried with (And they have all worked, and they are all siblings to the AppBarLayout):
Scrolling View
First ViewGroup inside a Scrollable View
ScrollView inside a ViewGroup
And this one didn't work:
ViewGroup with no Scrollable View children.
There are multiple examples online, but none of them really state where should you put it, like:
http://www.ingloriousmind.com/blog/quick-look-on-the-coordinatorlayout/
https://guides.codepath.com/android/Handling-Scrolls-with-CoordinatorLayout
https://developer.android.com/training/basics/firstapp/building-ui.html
https://www.bignerdranch.com/blog/becoming-material-with-android-design-support-library/
Check this link: https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html
AppBarLayout also requires a separate scrolling sibling in order to
know when to scroll. The binding is done through the
AppBarLayout.ScrollingViewBehavior 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.
They mentioned about that, it should be the View which will be shown under the AppBarLayout like this:
<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.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<!-- Your scrolling content -->
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<android.support.v7.widget.Toolbar
...
app:layout_scrollFlags="scroll|enterAlways"/>
<android.support.design.widget.TabLayout
...
app:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
My question is: in what exact ViewGroup (or maybe View) should we put
that app:layout_behavior?
And in this link: http://guides.codepath.com/android/Handling-Scrolls-with-CoordinatorLayout
Next, we need to define an association between the AppBarLayout and
the View that will be scrolled. Add an app:layout_behavior to a
RecyclerView or any other View capable of nested scrolling such as
NestedScrollView. The support library contains a special string
resource #string/appbar_scrolling_view_behavior that maps to
AppBarLayout.ScrollingViewBehavior, which is used to notify the
AppBarLayout when scroll events occur on this particular view. The
behavior must be established on the view that triggers the event.
Make sure you added the appbar_scrolling_view_behavior field in your String.xml
<!-- The class name to the ScrollingChildBehavior required for AppBarLayout -->
<string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>
And as everyone knows we just can use this like below
<android.support.v7.widget.RecyclerView
android:id="#+id/rvSomeList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
Its just for info not OP answer.
app:layout_behavior should be set to those views which are direct child of Coordinator layout
AppBarLayout also requires a separate scrolling sibling in order to
know when to scroll.
This description from Android is woefully incomplete and caused me hours of wasted time.
Scrolling sibling is a misnomer and need not be a scrolling view of any type.
For example, below my AppBarLayout, I'm using a ViewPager2 that will render a Fragment that will render a Scrollview, so I needed to set app:layout_behavior="#string/appbar_scrolling_view_behavior" directly on the ViewPager2 in the main layout, NOT the deeply nested Scrollview in the fragment layout.
I also have no use for scrolling the AppBarLayout or any of its children on or off the screen, so I falsely assumed I could get away with not setting the app:layout_behavior anywhere.
Wrong.
This reveals a more insidious issue: AppBarLayout requires the scrolling sibling, yes. But not just to "know when to scroll", but to actually adjust the size of the sibling to fit properly on screen alongside it! Otherwise, the sibling maintains its configured size and will be nudged downward offscreen by the height of the AppBarLayout! You can even see this in Android Studio's layout editor.
Long story short: If you're going to use an AppBarLayout, you need to mark one of your views with app:layout_behavior="#string/appbar_scrolling_view_behavior", whether it's a scroll view or not.
I had to add the following to the gradle file otherwise it gave me a compile error.
implementation 'com.google.android.material:material:1.0.0'
Hope this would help some others too!
For someone who uses CoordinatorLayout with FragmentContainer and AppBarLayout:
It is really good to set the app:layout_behavior also on the container (not just on NestedScrollView or RecyclerView). It deletes unnecessary bottom margin of the FragmentContainer and guarantees that the appbar hides when the keyboard is shown.

How to use Recyclerview inside Scrollview [duplicate]

This question already has answers here:
RecyclerView inside ScrollView is not working
(26 answers)
Closed 7 years ago.
I'm trying to show horizontal recyclerview items and vertical recyclerview items inside an ScrollView
Scrollview didn't work even If i use android:fillViewport="true"
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
//Horizontal Recyclerview items
<RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content">
</RecyclerView>
//vertical Recyclerview items
<RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"></RecyclerView>
</LinearLayout>
</ScrollView>
You should use NestedScrollView instead. However you may need to write your own LayoutManager. Check out this SO Answer for more details
You need to use a custom layout manager to use recyclerview inside a scrollview.
You can remove the scrollview and make a header item in the vertical recyclerview which contain the horizontal recyclerview.
Also you should not use a recyclerview inside a scrollview. So think the second approach will be better.
Also you can can use Snap-RecyclerView-Utils.
It has a linear layout manager for recyclerview inside a scroll view and an adapter which can help you make a header containing you horizontal recyclerview.
ScrollView can only have one child.
Remove your RelativeLayout and try again.
Apart from that android:layout_height in ScrollView should be set to wrap_content
Additionally I'm not quite sure, if it works, since in the Docs it is stated that
You should never use a ScrollView with a ListView, because ListView takes care of its own vertical scrolling. Most importantly, doing this defeats all of the important optimizations in ListView for dealing with large lists, since it effectively forces the ListView to display its entire list of items to fill up the infinite container supplied by ScrollView.
Maybe a NestedScrollView works since it is for
NestedScrollView is just like ScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.

Categories

Resources