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
,片段的根视图是 LinearLayout
,ConstraintLayout
围绕 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);
}
经过测试,运行良好。
我的主要 activity 中有一个持续的底部 sheet 行为。底部 sheet 有一个包含 ViewPager2
片段的容器。问题是 ViewPager2
阻止底部 sheet 垂直滚动。
我在示例应用程序中重现了该问题。从 this gif right here 中可以看出,如果垂直滚动在 ViewPager2
内则不起作用。只有当我一直向下拖动到 ViewPager2
之外时,它才会开始躲避底部 sheet。这使得滚动很尴尬。
我尝试了 CoordinatorLayout
,片段的根视图是 LinearLayout
,ConstraintLayout
围绕 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);
}
经过测试,运行良好。