BottomSheetBehavior with two RecyclerView

谁说胖子不能爱 提交于 2019-12-05 04:54:16

问题


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

回答1:


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.



来源:https://stackoverflow.com/questions/42159473/bottomsheetbehavior-with-two-recyclerview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!