BottomSheetDialogFragment在横屏模式下不能完全展示

34
我在我的活动中使用BottomSheetDialogFragment,当我切换到横屏模式时,对话框在纵向模式下显示完整高度,但在横向模式下不显示完整高度。

Portrait Mode

Landscape Mode

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        CustomBottomSheetDialog customBottomSheetDialog = new CustomBottomSheetDialog();
        customBottomSheetDialog.show(getSupportFragmentManager(),customBottomSheetDialog.getTag());
    }
}

自定义底部弹窗对话框

public class CustomBottomSheetDialog extends BottomSheetDialogFragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return View.inflate(getContext(), R.layout.view_config, null);
    }
}

自定义底部弹出框布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:background="#fdf107"
              android:layout_height="wrap_content">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="196dp"
        android:gravity="center"
        android:textColor="@color/colorAccent"
        android:text="BottomSheetDialogFragment"/>

</LinearLayout>

在横屏模式下,我必须拖动BottomSheetDialogFragment才能查看完整的内容。


2
原来这是预期行为(https://issuetracker.google.com/issues/37083487)。引用:“Material Design指南指出,底部工作表应在高度上窥视比底部工作表上方区域高19:6。由于您的横向屏幕比例低于16:9,因此它以规范中的最小高度窥视。” - Bracadabra
@Bracadabra,我没有看到你所提到的引用。 - clever_trevor
1
@clever_trevor 看起来那个评论已经被删除了。 - Bracadabra
7个回答

45

这个问题的解决方案是。

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT < 16) {
                view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
            FrameLayout bottomSheet = (FrameLayout)
            dialog.findViewById(android.support.design.R.id.design_bottom_sheet);
            BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
            behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            behavior.setPeekHeight(0); // Remove this line to hide a dark background if you manually hide the dialog.
        }
    });
}

4
事件触发后别忘了注销监听器。 - Eugen Pechanec
3
谢谢! 我遇到了这个问题:当对话框隐藏时,黑屏没有消失。 注释掉以下这行代码有帮助:behavior.setPeekHeight(0); - Sam Sch
16
如果您正在使用androidx库,您应该使用com.google.android.material.R.id.design_bottom_sheet - idish
我在代码中添加了一个取消监听器的操作。 - CoolMind
如果上述解决方案不起作用,请查看https://dev59.com/NlMH5IYBdhLWcg3w1Dr0#58067230 - Shvet
显示剩余4条评论

33

这个方法对我起作用,并且是最干净的方法:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = BottomSheetDialog(requireContext(), theme)
    dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
    return dialog
}

如果您能提供一些解释,例如为什么选择这种方法,那将更有帮助。请阅读https://stackoverflow.com/help/how-to-answer。 - 32cupo
2
在最近的API中,dialog.behavior没有被公开,它是私有的。 - Mohanakrrishna
它是公共的。刚刚查看了一下:dialog.behavior.state - MakinTosH
2
你也可以用 val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog 替换第二行。 - CoolMind
2
如果您在尝试关闭或隐藏对话框时遇到折叠状态,请添加以下内容:dialog.behavior.skipCollapsed = true - MakinTosH

17

感谢@avez raj和Prevent dismissal of BottomSheetDialogFragment on touch outside,我在onCreateDialog()中写入了代码。

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = super.onCreateDialog(savedInstanceState)

    dialog.setOnShowListener {
        val bottomSheet = dialog.findViewById<View>(
            com.google.android.material.R.id.design_bottom_sheet) as? FrameLayout
        val behavior = BottomSheetBehavior.from(bottomSheet)
        behavior.state = BottomSheetBehavior.STATE_EXPANDED
    }

    return dialog
}

目前我使用MakinTosH的解决方案,它更加简短。


2
为什么会发生这种情况?在哪些情况下会出现这种情况?有没有官方的处理方式?也许我们应该在未展开时以不同的方式处理它?或者这是一个错误吗?我也注意到Play Store上与此相关的奇怪错误:https://issuetracker.google.com/issues/133809189 - android developer
1
@androiddeveloper,好问题,我不知道。BSDF视图非常有bug,特别是涉及到键盘时。感谢你的研究。 - CoolMind
1
但即使没有键盘,它也被折叠成了更小的尺寸。 - android developer
2
@androiddeveloper,我猜想谷歌公司的工程师们每天都在制造奇怪和不可预测的组件。程序员们必须为每个错误创建解决方法。我的前同事也注意到了BSDF在全屏模式下与键盘的问题,甚至想用Fragment重写它。 - CoolMind
我明白了。谢谢。 - android developer
1
@androiddeveloper,如果他改变了代码,我可能会向他询问详细信息。 - CoolMind

6

ViewTreeObserver 的解决方案对我无效,但我在 这里 找到了一种更优越的解决方案,并将其转换为 Kotlin。这个解决方案没有 ViewTreeObserver 那样昂贵的计算浪费,并将功能完美地打包到类中。

class ExpandedBottomSheetDialog(context: Context) : BottomSheetDialog(context) {

    override fun show() {
        super.show()
        // androidx should use: com.google.android.material.R.id.design_bottom_sheet
        val view = findViewById<View>(R.id.design_bottom_sheet)
        view!!.post {
            val behavior = BottomSheetBehavior.from(view)
            behavior.setState(BottomSheetBehavior.STATE_EXPANDED)
        }
    }
}

5
这个解决方案不适用于 BottomSheetDialogFragment,因为它使用的 findViewById 不依附于任何视图(在这个阶段它是 null)。此外,如果你重写 show(manager: FragmentManager?, tag: String?) 方法(参见 https://dev59.com/VmUo5IYBdhLWcg3w2CWJ),并且使用 super.show(manager, tag),那么你可能会遇到异常。 - CoolMind

4
您可以简单地执行以下操作:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    dialog?.let {
        val sheet = it as BottomSheetDialog
        sheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED
    }

    // rest of your stuff
}

2
这是一个关于gestureInsetBottomIgnored的问题。对于横屏模式,该标志位为false,但对于竖屏模式则为true。

0

为什么添加了`onConfigurationChanged`后,它没有起作用(需要在横向模式下再次拖动)?

// support when screen rotate, resize etc.
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    rvLayoutManager.spanCount = getSpanCount()
    rvLayoutManager.requestLayout()
}

private fun getSpanCount(): Int {
    val widthPx = resources.displayMetrics.widthPixels
    val profileWidth = activity!!.resources.getDimension(R.dimen.profile_width)
    return Math.floor((widthPx / profileWidth).toDouble()).toInt()
}

这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - Abhishek Dutt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接