Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BottomSheetBehavior with two RecyclerView

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);
}
like image 265
elpatricko Avatar asked Feb 10 '17 12:02

elpatricko


2 Answers

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)
like image 175
lewkka Avatar answered Sep 18 '22 18:09

lewkka


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.

like image 31
anro Avatar answered Sep 21 '22 18:09

anro