BottomSheetDialogFragment - 允许子视图滚动

38

我有一个带有RecyclerViewBottomSheetDialogFragment。问题是,只要RecyclerView没有向上滚动,我想禁用BottomSheetDialogFragment的拖动关闭功能(当前我无法滚动RecyclerView,因为尝试滚动将始终关闭BottomSheetDialogFragment)。有什么办法可以实现这一点吗?


您可以将NestedScrollView用作RecyclerView的父级,这可能会有所帮助。 - Ajith Pandian
1
<fragment><NestedScrollView><RecyclerView/></NestedScrollView></fragment> - Ajith Pandian
1
这并没有帮助。而且 RecyclerView 已经支持作为一个嵌套滚动视图了... - prom85
有找到这个问题的解决方案吗?@prom85 - Morteza Rastgoo
你找到任何解决方案了吗?如果有,请分享一下。 - K Pradeep Kumar Reddy
13个回答

13

通过这种方式可以解决BottomSheetDialog中RecyclerView滚动问题。

来自: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>

2
这是一种不好的做法。分页调用被无限调用了。 - Sharukh Mohammed
@SharukhMohammed 这是正确的做法。在recyclerView中将nestedScrollingEnabled设置为false,并使用嵌套滚动视图而不是recyclerview进行分页处理。 - Ashwini

7

在扩展 RecyclerViewFragment 中,在 onCreateView 方法中尝试此操作。

recyclerView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        v.getParent().requestDisallowInterceptTouchEvent(true);
        v.onTouchEvent(event);
        return true;
    }
});

6

只需在您的布局中添加一个属性:

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

android:nestedScrollingEnabled="false" 可以完成所有工作。 此外,如果你用 SwipeRefreshLayout 包裹你的 RecyclerView - 刷新功能仍将继续工作。并且即使你使用更复杂的视图布局,你的 BottomSheetDialog 也将继续支持向下拖动关闭行为(如果滑动手势发生在 RecyclerView & SwipeRefreshLayout 外部)。


3

只需将它视为一个 BottomSheetDialog,并在触摸时禁用其拖动或滑动。

创建 BottomSheetDialog 时,它会自动将您的布局包装在CoordinatorLayout 中,因此如果您想从您的视图中获取行为,则调用

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

然后通过这个行为,你可以做你需要的事情。

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);
        }
    });

这对我很有帮助,希望能对你有所帮助。


你的答案对于展开状态是正确的,但如果我为我的bottomSheetDialog使用两种状态(折叠和展开),它就不起作用! - roghayeh hosseini
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); - Michael Osofsky
在我的情况下,它设置了一种奇怪的行为:当我滚动NestedScrollView时,一个BottomSheetDialog会反弹。我只是删除了BottomSheetBehaviorandroid:minHeight,然后NSV就可以在BottomSheetDialog的整个高度上滚动了。 - CoolMind

3
尝试这个解决方案:
    <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>

如果您没有使用RecyclerView,这是一种很好的选择。它允许您在滑动时进行滚动和解散操作,而不会相互重叠。 - Jcorretjer

2
当可回收视图具有滚动范围时,底部工作表将保持展开状态。但是,如果可回收视图没有向上滚动的滚动条,则从上方拖动将恢复正常行为。
         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) {
                    }
                });

当回收视图有很多项(填充高度)时有效。但如果我只有几个项目,我会卡在展开状态,无法向下滚动以将其最小化。 - Sunkas

1

更改setupDialog方法中BottomSheetDialogFragment的行为:

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) {

                }
            });
        }

2
这将完全禁用关闭拖动功能,但是我可以检查RecyclerView是否滚动到顶部,然后这应该可以工作。 - prom85

0

可以使用以下方法在BottomSheet中滚动RecyclerView:

  • 在XML文件中的RecyclerView中添加android:focusableInTouchMode="true"。

      <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" />
    

- 对于 BottomSheet,func onCreateDialog 中: + maxDesiredHeight 参数代表 BottomSheet 的固定高度。 + this.isDraggable = false -> 禁用 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
    }

- 为 RecyclerView 调用 setOnTouchListener 函数:

Recycler.setOnTouchListener { v, event ->
            v.parent.requestDisallowInterceptTouchEvent(true)
            v.onTouchEvent(event)
            true
        }

0

最近在我的项目中遇到了同样的问题,我尝试了这个和其他SO线程中提出的所有答案,但都没有成功,所以我决定用自己的方法解决它。

只是一个注意事项,我的情况是我有一个具有复杂布局的BottomSheetDialog,结果类似于这样

<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>

基本上需要的就是将BottomSheetBehavior添加到对话框的主布局中。
        _bottomSheetBehavior = BottomSheetBehavior.From((View)_mainLayout.Parent);

一旦我们拥有了这个,我们就创建一个自定义的BottomSheetCallback实现,并将其添加到行为中。
_bottomSheetCallback = new BottomSheetFullScreenCallback(_bottomSheetBehavior);
            _bottomSheetBehavior.AddBottomSheetCallback(_bottomSheetCallback);

最后一步是创建一个自定义的OntouchListener,并将其添加到RecyclerView中。
_recyclerView.SetOnTouchListener(new MyTouchListener(_bottomSheetBehavior));

现在我们已经准备好了一切,只需要管理recyclerView上的触摸事件。因此,在我们的自定义OnTouchListener中,我们以如下方式实现OnTouch方法。
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;
        }

完成这一步骤后,我们需要确保自定义BottomSheetCallback的OnSlide事件在bottomSheetBehavior不可拖动时不会被调用,我们可以通过以下方式实现:

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

就是这样了 ;)


0

我遇到了同样的情况,对我起作用的是在onCreate函数中只有一行代码。 你需要做的就是启用RecyclerView的嵌套滚动。

recyclerView.setNestedScrollingEnabled(true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接