ViewPager2 Inside bottom sheet 防止 bottom sheet 垂直滚动

ViewPager2 Inside bottom sheet prevents bottom sheet from vertically scrolling

我的主要 activity 中有一个持续的底部 sheet 行为。底部 sheet 有一个包含 ViewPager2 片段的容器。问题是 ViewPager2 阻止底部 sheet 垂直滚动。

我在示例应用程序中重现了该问题。从 this gif right here 中可以看出,如果垂直滚动在 ViewPager2 内则不起作用。只有当我一直向下拖动到 ViewPager2 之外时,它才会开始躲避底部 sheet。这使得滚动很尴尬。

我尝试了 中描述的解决方案,但没有任何改变。 activity 的根视图是 CoordinatorLayout,片段的根视图是 LinearLayoutConstraintLayout 围绕 ViewPager2

这是主要的 activity 布局:

<?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>

这是底部 sheet 布局中的片段:

<?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>

这是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();
    }
}

这里是 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));
        }
    }
}

感谢伟大的 bobekos 帮助我解决这个问题。他的回答是:

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

我翻译成Java:

RecyclerView innerRecyclerView = (RecyclerView) viewPager2.getChildAt(0);
if (innerRecyclerView != null) {
    innerRecyclerView.setNestedScrollingEnabled(false);
    innerRecyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
}

经过测试,运行良好。