在Activity的onBackPressed中如何实现BottomSheetDialogFragment下滑收起
问题说明
- 业务需求:自定义
BottomSheetDialogFragment子类VillazBottomSheet通过show()方法弹出底部面板后,按下系统返回键时,需要播放和手指下滑一致的收起动画,而非默认的直接关闭效果 - 现有面板打开代码:
VillazBottomSheet villazBottomSheet = new VillazBottomSheet(); villazBottomSheet.setArguments(bundle); villazBottomSheet.show(getSupportFragmentManager(), "VillazList");
- 预期返回键逻辑:调用面板的下滑收起方法,走自定义动画流程,而非默认关闭
具体实现步骤
1. 自定义BottomSheet中封装下滑收起能力
在VillazBottomSheet内部绑定BottomSheetBehavior,封装可控的下滑收起方法:
public class VillazBottomSheet extends BottomSheetDialogFragment { // 持有BottomSheet行为实例,控制面板状态 private BottomSheetBehavior<View> sheetBehavior; // 存储返回键回调,方便控制拦截开关 private OnBackPressedCallback backCallback; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.layout_villaz_bottom_sheet, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 拿到Dialog的根布局,绑定对应的Behavior View sheetParent = (View) view.getParent(); sheetBehavior = BottomSheetBehavior.from(sheetParent); // 添加状态监听,动画走完后自动安全关闭Fragment sheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { // 下滑到底隐藏状态时,动画已完成,再触发Fragment关闭 if (newState == BottomSheetBehavior.STATE_HIDDEN) { dismissAllowingStateLoss(); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { // 滑动过程不需要额外逻辑可留空 } }); } /** * 对外暴露的下滑收起方法,触发和用户手动下滑一致的动画 */ public void slidedown() { if (sheetBehavior != null) { sheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); } } }
2. 拦截返回键事件
推荐使用AndroidX提供的OnBackPressedDispatcher处理返回逻辑,兼容Android 13以上版本,且不需要在Activity中手动判断面板显示状态:
在VillazBottomSheet的onCreate方法中注册返回拦截回调,和Fragment生命周期绑定,避免内存泄漏:
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化返回拦截回调,默认启用 backCallback = new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { // 回调触发时直接调用下滑收起方法 slidedown(); } }; // 把回调绑定到当前Fragment的生命周期,自动随Fragment销毁移除 requireActivity().getOnBackPressedDispatcher().addCallback(this, backCallback); }
注意:这个回调和Fragment的Lifecycle绑定,只有当VillazBottomSheet处于用户可见的显示状态时,返回事件才会被拦截;当面板关闭、Fragment进入后台状态后,回调会自动失效,不会影响Activity本身的返回逻辑。
兼容旧版:重写onBackPressed实现
如果项目还没有适配AndroidX的返回回调,需要在Activity中手动判断面板显示状态后触发逻辑:
@Override public void onBackPressed() { // 通过Tag查找已添加的Fragment实例,不要持有全局Fragment引用(避免配置变更后引用失效) VillazBottomSheet sheet = (VillazBottomSheet) getSupportFragmentManager().findFragmentByTag("VillazList"); if (sheet != null && sheet.isVisible()) { // 面板显示时触发下滑收起 sheet.slidedown(); } else { // 面板隐藏时走原本的返回逻辑 super.onBackPressed(); } }
注意事项
- 不要直接调用
dismiss()方法关闭面板:直接调用dismiss会跳过状态动画,无法触发和用户手动下滑一致的动画效果。使用setState(STATE_HIDDEN)的方式可以完全复用系统自带的下滑动画链路,触发所有已注册的状态监听。 - 不要在Activity中持有
VillazBottomSheet的全局成员变量:当发生屏幕旋转、系统语言切换等配置变更时,Fragment会被系统重建,全局引用会指向已经销毁的旧实例,引发空指针或者内存泄漏问题,通过findFragmentByTag获取实例是更可靠的方式。
内容的提问来源于stack exchange,提问作者Vora Ravi
相关产品推荐
相关产品推荐

