You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

切换ViewPager碎片时BottomSheetDialogFragment被导航栏遮挡

问题描述

我的BottomSheetDialogFragment初始显示正常,悬浮在导航栏上方。
初始视图

点击继续切换新视图后,BottomSheetDialogFragment会延伸到导航栏后方,底部按钮被遮挡,手动上滑可让其覆盖导航栏。
按钮被导航栏遮挡

手动上滑后的显示效果如下:
手动上滑后的底部弹窗

经排查,问题与com.stripe.android.view.CardFormView相关——移除该视图后显示完全正常,只有当这个视图可见时,底部按钮才会错位。请问如何让底部弹窗在加载新视图时自动重置布局位置,避免被导航栏遮挡?


相关代码

TPCollectPaymentDialog.kt

class TPCollectPaymentDialog() : BottomSheetDialogFragment() {

    private var _binding: BottomSheetDialogBinding? = null
    private val binding get() = _binding!!


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setStyle(STYLE_NORMAL, R.style.BottomSheetDialog)
    }


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        _binding = BottomSheetDialogBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.viewPager2.orientation = ViewPager2.ORIENTATION_HORIZONTAL
        binding.viewPager2.adapter = TPStandardFlowPageAdapter(this)
        binding.viewPager2.isUserInputEnabled = false
    }



    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

}

bottom_sheet_dialog.xml

<androidx.constraintlayout.widget.ConstraintLayout 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.cardview.widget.CardView
        android:id="@+id/card_view_dialog_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="0dp"
        android:clipChildren="true"
        android:padding="0dp"
        app:cardCornerRadius="16dp"
        app:cardUseCompatPadding="true">

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewPager2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

        </androidx.viewpager2.widget.ViewPager2>
    </androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

ManualEntryFragment.kt

class ManualEntryFragment : Fragment(), BluetoothReaderListener {
    private var _binding: CardManualPaymentBinding? = null
    private val binding get() = _binding!!
    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        _binding = CardManualPaymentBinding.inflate(inflater, container, false)
        return binding.root
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.tvErrorMessage.remove()
        binding.progressBar.remove()
        binding.btnCancel.show()
    }

    override fun onResume() {
        super.onResume()
        binding.root.requestLayout()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

manual_card_fragment.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="0dp"
    android:animateLayoutChanges="true"
    android:gravity="center_horizontal"
    android:keepScreenOn="true"
    android:orientation="vertical"
    android:padding="0dp">

      <!-- 为简洁起见移除其他元素 -->

        <com.stripe.android.view.CardFormView
            android:id="@+id/card_form_view"
            style="@style/StripeCardFormView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />


        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:progressTint="@color/colorPrimary"
            />

        <Button
            android:id="@+id/btnCheckout"
            android:layout_width="match_parent"
            android:layout_height="60dp" />

        <Button
            android:id="@+id/btnCancel"
            android:layout_width="match_parent"
            android:layout_height="54dp" />
</LinearLayout>

解决方案

针对CardFormView导致BottomSheet布局错位的问题,可尝试以下几种方法:

1. 强制BottomSheet重新计算布局

在ManualEntryFragment的onResume中延迟触发BottomSheet的状态重置,确保CardFormView完成渲染:

override fun onResume() {
    super.onResume()
    view?.postDelayed({
        val dialog = requireDialog() as BottomSheetDialog
        val bottomSheetBehavior = BottomSheetBehavior.from(dialog.findViewById(R.id.design_bottom_sheet))
        // 先收缩再展开,触发布局重算
        bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
        bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
    }, 100)
}

2. 调整BottomSheet窗口适配规则

在TPCollectPaymentDialog的onStart方法中,设置窗口适配系统导航栏,同时锁定BottomSheet的适配行为:

override fun onStart() {
    super.onStart()
    val dialog = dialog as BottomSheetDialog
    val window = dialog.window
    window?.decorView?.apply {
        // 让布局稳定适配导航栏
        systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    }
    // 强制BottomSheet适配内容高度
    val bottomSheetBehavior = BottomSheetBehavior.from(dialog.findViewById(R.id.design_bottom_sheet))
    bottomSheetBehavior.isFitToContents = true
    bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}

3. 监听CardFormView布局完成事件

在ManualEntryFragment中监听CardFormView的布局完成事件,完成后立即触发BottomSheet重布局:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // ...其他代码
    binding.cardFormView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            binding.cardFormView.viewTreeObserver.removeOnGlobalLayoutListener(this)
            val dialog = requireDialog() as BottomSheetDialog
            BottomSheetBehavior.from(dialog.findViewById(R.id.design_bottom_sheet)).apply {
                requestLayout()
                state = BottomSheetBehavior.STATE_EXPANDED
            }
        }
    })
}

4. 调整布局适配导航栏

修改manual_card_fragment.xml的根布局,添加导航栏高度的底部内边距,确保按钮不被遮挡:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="0dp"
    android:animateLayoutChanges="true"
    android:gravity="center_horizontal"
    android:keepScreenOn="true"
    android:orientation="vertical"
    android:paddingBottom="?android:attr/navigationBarSize"
    android:padding="0dp">

内容的提问来源于stack exchange,提问作者user-44651

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 11:17:54