BottomSheetDialogFragment - Allow scrolling child
Asked Answered
B

13

38

I have a BottomSheetDialogFragment with a RecyclerView. The problem is, I want to disable the drag close function of the BottomSheetDialogFragment as long as the RecyclerView is not scrolled up (currently I can't scroll my RecyclerView as the attempt will always close the BottomSheetDialogFragment). Any ideas how to achieve this?

Bunyip answered 1/12, 2016 at 7:19 Comment(3)
You can use NestedScrollView as a parent of RecyclerView. It may help.Riess
<fragment><NestedScrollView><RecyclerView/></NestedScrollView></fragment>Riess
This does not help. And RecyclerView supports being a nested scrolling view out of the box itself already...Bunyip
K
13

RecyclerView scrolling problem in BottomSheetDialog can be solved by this way.

from: https://android.jlelse.eu/recyclerview-within-nestedscrollview-scrolling-issue-3180b5ad2542

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

<android.support.v4.widget.NestedScrollView
        android:id="@+id/nestedScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:overScrollMode="never">


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</LinearLayout>
Kosygin answered 20/2, 2019 at 0:40 Comment(2)
This is a bad way of doing it. Pagination calls are infinitely called.Aluminothermy
@SharukhMohammed this is right way of doing. put nestedScrollingEnabled as false in recyclerView and do the pagination thing with nested scroll view not with recyclerviewAmylopsin
G
8

Try this in your Fragment that extends RecyclerView in onCreateView

recyclerView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        v.getParent().requestDisallowInterceptTouchEvent(true);
        v.onTouchEvent(event);
        return true;
    }
});
Gynaeceum answered 24/9, 2020 at 12:8 Comment(0)
A
6

Just add one (!) attribute to your layout:

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/receiversList"
    android:nestedScrollingEnabled="false"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

android:nestedScrollingEnabled="false" doing all the job. Also, if you wrap around your RecyclerView with SwipeRefreshLayout - refresh function will continue to work. And even you put your layout with more complex Views, your BottomSheetDialog will continue to support drag-down-to-dismiss behaviour (If touch with swipe down gesture occurs outside of RecyclerView & SwipeRefreshLayout).

Arceliaarceneaux answered 13/10, 2020 at 6:36 Comment(0)
G
3

Just treat it as a BottomSheetDialog , and simply disable its dragging or sliding when touch .

When create a BottomSheetDialog , it will automatically wrap your layout in a CoordinatorLayout , so if you want to get a behaviour from your view , call

final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());

Then through this behaviour, you can do what you need.

final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
    behavior.setHideable(false);
    behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View bottomSheet, int newState) {

        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
            behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }
    });

This worked for me.Hope it helps.

Geithner answered 17/8, 2017 at 7:21 Comment(3)
your answer is true for expanded state, but if i use tow state for my bottomSheetDialog (collapse & expand) it dos not work!Idonah
behavior.setHideable(false); helped for me, it prevents scrolling the bottom sheet. However, when the user taps the back button, I call behavior.setHideable(true); so that I can then call behavior.setState(BottomSheetBehavior.STATE_HIDDEN);Tipple
In my case it sets a strange behaviour: when I scroll NestedScrollView, a BottomSheetDialog bounces. I simply removed BottomSheetBehavior and android:minHeight, then NSV became scrollable over full height of the BottomSheetDialog.Ergonomics
W
3

try this solution :

    <androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/layout_form"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

    <!-- elements-->
    <!-- elements-->

    </LinearLayout>

</androidx.core.widget.NestedScrollView>
Waites answered 18/1, 2021 at 11:41 Comment(1)
This is the way to go, if you're not using a RecyclerView. It'll allow you to scroll and dismiss by swiping without overlapping each other.Ayn
H
2

When recycle view has scope to scroll, bottom sheet will remain expanded. But if recycle view has no scroll to up direction, than drag from up, will revert normal behaviour.

         behavior.setState(BottomSheetBehavior.STATE_EXPANDED);

                behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
                    @Override
                    public void onStateChanged(@NonNull View bottomSheet, int newState) {
                        if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                            dismiss();
                        }

                        if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                            if(!recyclerView.canScrollVertically(1)) {
                                behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                            }
                        }
                    }

                    @Override
                    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                    }
                });
Hexone answered 18/6, 2020 at 22:32 Comment(1)
Works when the recycler view has a lot of items (fills the height). But if I have just a few items, I get stuck in the expanded state and can't scroll down to minimize it.Moderation
P
1

Change the Behaviour in the BottomSheetDialogFragment in setupDialog method:

CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
        final CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
        if (behavior != null && behavior instanceof BottomSheetBehavior) {
            ((BottomSheetBehavior) behavior).setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
                @Override
                public void onStateChanged(@NonNull View bottomSheet, int newState) {
                    if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                        dismiss();
                    }

                    if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                        ((BottomSheetBehavior) behavior).setState(BottomSheetBehavior.STATE_EXPANDED);
                    }


                }

                @Override
                public void onSlide(@NonNull View bottomSheet, float slideOffset) {

                }
            });
        }
Precontract answered 1/12, 2016 at 7:38 Comment(1)
this will disable the close drag functionality completely, but I can check if the RecyclerView is scrolled to top as well, then this should workBunyip
B
0

I had the same situation and what worked for me is just a single line of code inside onCreate function. What you have to do is just enable recyclerViews nested scrolling.

recyclerView.setNestedScrollingEnabled(true);
Bola answered 23/10, 2021 at 8:24 Comment(0)
Y
0

I had the same problem recently on my project and I have tried all the answer suggested in this and in other threads on SO but without luck so I decided to solve it my way.

Just a note, my situation it's that I have a BottomSheetDialog with a complex layout that result in something like this

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="_mainLayout"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <View
        android:id=" />

    <LinearLayout
        android:id="">
            <ImageView
                android:id=""/>
            <LinearLayout
                android:id="">
                <TextView
                    android:id="" />
                <TextView
                    android:id=""/>
            </LinearLayout>
            <LinearLayout
                android:id="">
                <TextView
                        android:id=""/>
            </LinearLayout>

        </LinearLayout>
        <android.support.v7.widget.RecyclerView
            android:layout_marginTop="10dp"
            android:id=""
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
        <Button
            android:id="">
        <Button
            android:id="">
</LinearLayout>

Basically what it's needed it's to add a BottomSheetBehavior to the main layout of the dialog

        _bottomSheetBehavior = BottomSheetBehavior.From((View)_mainLayout.Parent);

once we have this we create a custom BottomSheetCallback implementation and we add it to the behavior

_bottomSheetCallback = new BottomSheetFullScreenCallback(_bottomSheetBehavior);
            _bottomSheetBehavior.AddBottomSheetCallback(_bottomSheetCallback);

last step it's to create a custo OntouchListener that we will add to the RecyclerView

_recyclerView.SetOnTouchListener(new MyTouchListener(_bottomSheetBehavior));

Now we have everything in place and we just need to manage the touch on the recyclerView. So in our custom OnTouchListener we implement the OnTouch method in this way

public bool OnTouch(View view, MotionEvent e)
        {
            if (e.Action == MotionEventActions.Down || e.Action == MotionEventActions.Move)
            {
                _bottomSheetBehavior.Draggable = false;
            }
            if (e.Action == MotionEventActions.Up)
            {
                _bottomSheetBehavior.Draggable = true;
            }
            return true;
        }

Once we have done this we need to be sure that the OnSlide event of the custom BottomSheetCallback will never be called once the bottomSheetBehavior it's not draggable, and we can do this in the following way:

public override void OnSlide(View bottomSheet, float slideOffset)
        {
            if (_bottomSheetBehavior.Draggable)
            {
                OnSlideEvent?.Invoke(this, slideOffset);
                
            }
        }

And that's it ;)

Yarbrough answered 25/8, 2022 at 14:52 Comment(0)
V
0

To scroll RecyclerView inside BottomSheet can use this way:

  • Add android:focusableInTouchMode="true" to RecyclerView in file XML.

      <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/recycler"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_marginTop="5dp"
          android:clipToPadding="false"
          android:focusableInTouchMode="true"
          android:paddingBottom="50dp" />
    

- In func onCreateDialog for BottomSheet: + where: maxDesiredHeight is set fixed size of BottomSheet. + this.isDraggable = false -> disabling dragging on BottomSheet.

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
        dialog.setOnShowListener {
            dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)!!
                .apply {
                    val maxDesiredHeight = (resources.displayMetrics.heightPixels * 0.95).toInt()
                    val bottomSheetLayoutParams = this.layoutParams
                    bottomSheetLayoutParams.height = maxDesiredHeight
                    this.layoutParams = bottomSheetLayoutParams
                    BottomSheetBehavior.from(this).apply {
                        this.state = BottomSheetBehavior.STATE_EXPANDED
                        this.isDraggable = false
                    }
                }
        }
        return dialog
    }

- Call the setOnTouchListener function for RecyclerView:

Recycler.setOnTouchListener { v, event ->
            v.parent.requestDisallowInterceptTouchEvent(true)
            v.onTouchEvent(event)
            true
        }
Visconti answered 30/1, 2023 at 4:38 Comment(0)
R
0

Just wrap your layout into NestedScrollView and add this attribute in to the RecyclerView

android:nestedScrollingEnabled="true"
Rohn answered 18/7, 2023 at 10:41 Comment(0)
E
-1

With Constraint layout

<androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_team_mates"
            android:layout_width="@dimen/dp_0"
            android:layout_height="@dimen/dp_0"
            android:layout_marginVertical="@dimen/dp_20"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/txt_title_custom"
            app:layout_constraintBottom_toBottomOf="parent"/>
Esmeraldaesmerelda answered 9/9, 2021 at 14:10 Comment(1)
Please provide additional details in your answer. As it's currently written, it's hard to understand your solution.Hatpin
A
-4

Put your RecyclerView under NestedScrollView where NestedScroll is parent of recycler

Amylopsin answered 27/7, 2019 at 7:19 Comment(1)
this will not make any different, still closes the fragment !!Encourage

© 2022 - 2024 — McMap. All rights reserved.