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