Scroll not working for multiple RecyclerView in BottomSheet

As mentioned by R. Zagórski, I described the reason for this scrolling behavior here, i.e., BottomSheetBehavior only supports one scrolling child. However this answer wasn't focusing on Bottom Sheet Dialogs.

Therefore – just like R. Zagórski – I extended my own library that overcomes this limitation. Starting with 0.0.3 there is support for Bottom Sheet Dialogs! You can find the library and the example app here: https://github.com/laenger/ViewPagerBottomSheet

To use in your project, simply add the maven repo url to your build.gradle:

repositories {
    maven { url "https://raw.github.com/laenger/maven-releases/master/releases" }
}

Add the library to the dependencies:

dependencies {
    compile "biz.laenger.android:vpbs:0.0.3"
}

Use ViewPagerBottomSheetDialogFragment as super class for Dialog Fragments. Then setup any ViewPager inside the content view:

public class DialogFragment extends ViewPagerBottomSheetDialogFragment {
    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);
        final View contentView = View.inflate(getContext(), R.layout.dialog_bottom_sheet, null);

        final ViewPager viewPager = (ViewPager) contentView.findViewById(R.id.viewpager);
        // ...
        BottomSheetUtils.setupViewPager(viewPager);

        dialog.setContentView(contentView);
    }
}

sample implementation


When trying to look for the problem on StackOverflow I found this thread. It depicts the bug (at least that is how I look at it), that BottomSheetBehaviour works only for the first scrollable child it finds. It also proposes the usage of different CoordinatorLayout.Behavior proposed and published here.

However, your case is a bit different. BottomSheetDialogFragment is used. And this is where the provided solution does not work. However, I managed to overcome this problem. Published repository, where your project was modified to be working. It uses the ViewPagerBottomSheetBehavior from the library mentioned earlier.

Basically, the following changes were made:

  1. StatisticFragment extends ViewPagerBottomSheetDialogFragment and not BottomSheetDialogFragment
  2. The onCreateDialog function in StatisticsFragment is changed:

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ViewPagerBottomSheetDialog dialog = (ViewPagerBottomSheetDialog) super.onCreateDialog(savedInstanceState);
        View rootView = View.inflate(getContext(), R.layout.sheet_main, null);
        viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
        tabLayout = (TabLayout) rootView.findViewById(R.id.tabs);
        dialog.setContentView(rootView);
        mBehavior = ViewPagerBottomSheetBehavior.from((View) rootView.getParent());
        mBehavior.setPeekHeight(400);
        if (viewPager != null && tabLayout != null) {
            initViewPager();
        }
        return dialog;
    }
    
  3. The following function is invoked on the ViewPager:

    BottomSheetUtils.setupViewPager(viewPager);
    

And that is all. The project works.

The following is done behind the scenes:

BottomSheetDialogFragment has only one method:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new BottomSheetDialog(getContext(), getTheme());
}

There BottomSheetDialog is returned. However, it has statically defined behaviour set to BottomSheetBehavior. What was needed was to override ViewPagerBottomSheetDialogFragment to return ViewPagerBottomSheetDialog where it's CoordinatorLayout.Behavior is set to ViewPagerBottomSheetBehavior. Also, the custom BottomSheet was needed to be overriden to accustom to ViewPagerBottomSheetBehavior.


I had the same issue, to fix this without the need to override BottomSheetBehavior or the need of an additional library you can to the following: Implement a callback inside your bottom sheet implementation that registers changes of the page.

fun onPageChanged(currentPage: Int) {
   recycler1.isNestedScrollingEnabled = currentPage == 0
   recycler2.isNestedScrollingEnabled = currentPage == 1
   dialog?.findViewById<FrameLayout>(R.id.design_bottom_sheet)?.requestLayout()
}

In the BottomSheetBehavior implementation in onLayoutChild a lookup for the first child that supports nested scrolling is performed, with this change the lookup is repeated. Not optimal solution but works fine in my case


use this view as root view:

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class DisallowInterceptView extends LinearLayout {
    public DisallowInterceptView(Context context) {
        super(context);
        requestDisallowInterceptTouchEvent(true);
    }

    public DisallowInterceptView(Context context, AttributeSet attrs) {
        super(context, attrs);
        requestDisallowInterceptTouchEvent(true);
    }

    public DisallowInterceptView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        requestDisallowInterceptTouchEvent(true);
    }


    public boolean dispatchTouchEvent(MotionEvent ev) {
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                requestDisallowInterceptTouchEvent(false);
                break;
        }
        return super.onTouchEvent(event);
    }

}

then in your layout that used for bottmSheet:

<?xml version="1.0" encoding="utf-8"?>
<com.your.package.DisallowInterceptView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:maxHeight="400dp"
    android:minHeight="300dp"
    android:orientation="vertical"
    >

    ...
</LinearLayout>

</com.your.package.DisallowInterceptView>