6

I got two RecyclerView inside a LinearLayout with a BottomSheetBehavior. When you click on a item inside the first RecyclerView (with a grid) the RecyclerView is set to Gone and an the second RecyclerView (with a list) is shown. When the second Recycler is shown you cant slide the BottomSheet up and down instead the List is scrolling even in Expanded State. If the First Recycler is up everything is fine. Is there a way to make the BottomSheet to slide up and down again?

<LinearLayout
        android:id="@+id/sliding_layout_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:orientation="vertical"
        app:behavior_hideable="false"
        app:behavior_peekHeight="400dp"
        app:layout_behavior="@string/bottomSheetBehavior">

        <android.support.v7.widget.RecyclerView 
           android:id="@+id/grid"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:layout_marginEnd="@dimen/activity_horizontal_margin"
           android:layout_marginStart="@dimen/activity_horizontal_margin"
           android:background="@color/white"
           android:clickable="true"
           android:scrollbars="none" />

        <android.support.v7.widget.RecyclerView 
           android:id="@+id/list"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:layout_marginEnd="@dimen/activity_horizontal_margin"
           android:layout_marginStart="@dimen/activity_horizontal_margin"
           android:background="@color/white"
           android:clickable="true"
           android:scrollbars="none" />
</LinearLayout>

GridAdapter:

   @Override
   public void onBindViewHolder(ViewHolder holder, int position) {

    String categorieName = mCategories.get(position);
    final CategoryFilterEvent event = new   CategoryFilterEvent(categorieName);
    holder.grid_item_label.setText(categorieName);

    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            EventBus.getDefault().post(event);
        }
    });
}

MainActivity:

 @Override
 public void onCreate(Bundle savedInstanceState) {

    linearLayoutManager = new LinearLayoutManager(this);
    listAdapter = new ListAdapter(this, mList);
    recyList.setAdapter(listAdapter);
    recyList.setLayoutManager(linearLayoutManager);

    gridLayoutManager = new GridLayoutManager(this, 3);
    gridAdapter = new GridAdapter(this, new ArrayList<String>());
    recyGrid.setAdapter(gridAdapter);
    recyGrid.setLayoutManager(gridLayoutManager);
}

public void onEventMainThread(CategoryFilterEvent event) {
    recyGrid.setVisibilty(GONE);
    recyList.setVisiblity(VISIBLE);
}
elpatricko
  • 488
  • 5
  • 17
  • i think the problem is that the bottom sheet gets slide(up and down ) when you slide your finger on it but when the recycler view gets inside the bottom sheet the fling (swipe through finger ) action gets consumed by recycler view , I think that is your problem if you are putting recyler view inside bottom sheet. – A.s.ALI Feb 10 '17 at 12:53
  • But it works perfectly for the first RecyclerView. – elpatricko Feb 10 '17 at 12:56
  • share some code – A.s.ALI Feb 10 '17 at 12:56
  • See edit. But i dont think actual code will help. – elpatricko Feb 10 '17 at 13:07
  • yes it is not helping. I will need some code and detail information relating to how u are entering the activity , where is first recycler view and where does the 2nd recycler view appear? – A.s.ALI Feb 14 '17 at 05:13
  • you can use in CoordinatorLayout, check this [link](https://stackoverflow.com/a/46681326/4797289). – Rasoul Miri Oct 11 '17 at 06:45

3 Answers3

14

Do you have two recyclerViews inside BottomSheetDialogFragment(first – horizontal, second – vertical) and second recyclerView not scrollable? Just add this to the first recycler after you set an adapter to it:

ViewCompat.setNestedScrollingEnabled(recyclerView1, false)
lewkka
  • 1,019
  • 15
  • 25
  • Wow! I just implemented this solution without seeing your answer and I feel really happy now that I was able to come up with this approach on my own! – Devansh Maurya Nov 03 '20 at 17:15
  • 1
    Thank you! Can anyone explain why this works? This solution also worked by assigning `android:nestedScrollingEnabled="false"` on the first RecyclerView in xml. – Cole Tustin Feb 20 '22 at 21:50
  • Thank a lot for your answer, but can someone explain why does it work? – Denisque Mar 23 '22 at 13:22
  • @Denisque the default behavior of the bottom sheet is for nested scrolling to only work for the first recyclerview it finds. it is most likely a limitation from outdated flows where Android assumed it would only contain at best 1 scroll view inside. – Shadow Dec 29 '22 at 16:31
7

The implementation of BottomSheetBehavior doesn't support two scrollable views inside, that's why this layout will never work "out of the box". However, there is a hacky, but simple workaround to this problem. First, we have to make custom BottomSheetBehavior by copying that class' code to our new CustomBottomSheetBehavior class. Than, modify "onLayoutChild" method by replacing the line

mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));

with

if (mNestedScrollingChildRef == null) {
    mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
}

mNestedScrollingChildRef has package level access in BottomSheetBehavior class, so there is no way we can extend it.

Than, add following method:

public void setNestedScrollingChildRef(View v) {
    this.mNestedScrollingChildRef = new WeakReference<View>(v);
}

And in your Activity class:

 RecyclerView recyGrid = (RecyclerView)findViewById(R.id.grid);
 RecyclerView recyList = (RecyclerView)findViewById(R.id.list);
 layout = (LinearLayout)findViewById(R.id.sliding_layout_container);

 recyGrid.addOnItemTouchListener(onItemTouchListener);
 recyList.addOnItemTouchListener(onItemTouchListener);

onItemTouchListener code:

RecyclerView.OnItemTouchListener onItemTouchListener = new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        setScrollable(layout, rv);
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
};

private void setScrollable(View bottomSheet, RecyclerView recyclerView){
    ViewGroup.LayoutParams params = bottomSheet.getLayoutParams();
    if (params instanceof CoordinatorLayout.LayoutParams) {
        CoordinatorLayout.LayoutParams coordinatorLayoutParams = (CoordinatorLayout.LayoutParams) params;
        CoordinatorLayout.Behavior behavior = coordinatorLayoutParams.getBehavior();
        if (behavior != null && behavior instanceof CustomBottomSheetBehavior)
            ((CustomBottomSheetBehavior)behavior).setNestedScrollingChildRef(recyclerView);
    }
}

What we do here is catching all touch events, that come to recyclerViews and add it as a mNestedScrollingChildRef to CustomBottomSheetBehavior class, so that all scrolling events could be handled in a right way.

Update of 27.02.2018 Using this approach with BottomSheetDialogFragment involves further copy-pasting. We should create CustomBottomSheetDialog that extends AppCompatDialog and copy there all the code from class BottomSheetDialog. Then change there class variable mBehavior to be of CustomBottomSheetBehavior, that I described above.

Then, modify "wrapInBottomSheet" method to set new behavior there:

ViewGroup.LayoutParams bottomSheetParams = bottomSheet.getLayoutParams();
    if (bottomSheetParams instanceof CoordinatorLayout.LayoutParams) {
        mBehavior = new CustomBottomSheetBehavior<>();
        mBehavior.setBottomSheetCallback(mBottomSheetCallback);
        mBehavior.setHideable(mCancelable);
        mBehavior.setPeekHeight(*some value here*);
        ((CoordinatorLayout.LayoutParams) bottomSheetParams).setBehavior(mBehavior);
    }

And in your fragment class override "onCreateDialog" method to use CustomBottomSheetDialog there:

  @NonNull
  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState) {
    CustomBottomSheetDialog  dialog = new CustomBottomSheetDialog (getActivity(), R.style.YourDialogTheme);
    dialog.setContentView(R.layout.bottom_sheet_page_fragment);

     RecyclerView recyGrid = (RecyclerView)dialog.findViewById(R.id.grid);
     RecyclerView recyList = (RecyclerView)dialog.findViewById(R.id.list);
     layout = (LinearLayout)dialog.findViewById(R.id.sliding_layout_container);

     recyGrid.addOnItemTouchListener(onItemTouchListener);
     recyList.addOnItemTouchListener(onItemTouchListener);
     return dialog;
    }

The rest of the code remains the same.

anro
  • 1,300
  • 16
  • 30
  • how use this for BottomSheetDialogFragment? – Rasoul Miri Oct 09 '17 at 13:51
  • @RasoulMiri I've edited my answer to explain how to use this with BottomSheetDialogFragment – anro Feb 27 '18 at 05:48
  • 1
    This is not a helpful solution, since it lacks many informations. As you wrote: It is not possible to access the class from outside the package. So its not possible to use any of your methods written above. Since there are also other package only functions, you cannot simply copy and paste the entire behavior class to another file. – 8K Creative Commons Jul 04 '19 at 14:28
6

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) {
   nestedScrollView1.isNestedScrollingEnabled = currentPage == 0
   nestedScrollView2.isNestedScrollingEnabled = currentPage == 1
   ...
   dialog?.findViewById<View>(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

luktant
  • 121
  • 1
  • 6