I have a a persistent bottom sheet behavior in my main activity. The bottom sheet has a container for a fragment with a ViewPager2
. The problem is that the ViewPager2
prevents the bottom sheet from vertically scrolling.
I recreated the issue in the a sample app. As you can see from this gif right here, the vertical scrolling doesn't work if it's inside the ViewPager2
. Only when I drag all the way down outside the ViewPager2
, does it start ducking the bottom sheet. This makes scrolling awkward.
I tried the solution described here but it didn't change anything. The activity's root view is a CoordinatorLayout
and the fragment's root view is a LinearLayout
with a ConstraintLayout
around the ViewPager2
.
Here's the main activity layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center">
<Button
android:id="@+id/expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Expand Bottom Sheet" />
</LinearLayout>
<FrameLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Here's the fragment inside the bottom sheet's layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/purple_500">
<TextView
android:id="@+id/sheet_textview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:gravity="center_horizontal"
android:text="This is the sheet fragment"
android:textSize="24sp"
android:textAlignment="center"
android:textColor="@color/white" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="325dp"
android:layout_marginTop="75dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_horizontal"
android:text="Outside viewpager"
android:textSize="24sp"
android:textAlignment="center"
android:textColor="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/view_pager"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
Here's the MainActivity
:
package com.example.myapplication;
import android.os.Bundle;
import android.widget.Button;
import android.widget.FrameLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomSheetBehavior<FrameLayout> bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
bottomSheetBehavior.setPeekHeight(200);
bottomSheetBehavior.setHideable(true);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
Button expandButton = findViewById(R.id.expand_button);
expandButton.setOnClickListener(view -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED));
Fragment sheetFragment = new SheetFragment();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, sheetFragment).commit();
}
}
And here's the SheetFragment
:
package com.example.myapplication;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
public class SheetFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment_sheet, container, false);
ViewPager2 viewPager = contentView.findViewById(R.id.view_pager);
ViewPagerAdapter adapter = new ViewPagerAdapter();
viewPager.setAdapter(adapter);
return contentView;
}
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerHolder> {
@NonNull
@Override
public ViewPagerHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = getLayoutInflater().inflate(R.layout.pager_item, parent, false);
return new ViewPagerHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ViewPagerHolder holder, int position) {
holder.bind(position);
}
@Override
public int getItemCount() {
return 5;
}
}
private class ViewPagerHolder extends RecyclerView.ViewHolder {
public ViewPagerHolder(@NonNull View itemView) {
super(itemView);
}
public void bind(int number) {
TextView textView = itemView.findViewById(R.id.pager_textview);
textView.setText(String.format("Inside ViewPager\nPage #%d", number));
}
}
}
Thanks to the great bobekos for helping me with this. His answer was:
Thats because the bottomsheet allow only one scrollable view. So to make it work you must disable the nestedscrolling of the viewpager. The problem is you must get access to the recylerview inside the viewpager2 there is currently no get method and the class is final so you can't use inheritance. But you can do this:
//as extension function fun ViewPager2.disableNestedScrolling() { (getChildAt(0) as? RecyclerView)?.apply { isNestedScrollingEnabled = false overScrollMode = View.OVER_SCROLL_NEVER } }
ps.not tested directly
I translated it to Java:
RecyclerView innerRecyclerView = (RecyclerView) viewPager2.getChildAt(0);
if (innerRecyclerView != null) {
innerRecyclerView.setNestedScrollingEnabled(false);
innerRecyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
Tested and works perfectly.