BottomSheetDialogFragment - 如何设置扩展高度(或最小顶部偏移量)

86
我创建了一个BottomSheetDialogFragment,并且我想调整它的最大展开高度。我该如何做呢?我可以检索到BottomSheetBehavior,但是我只能找到ピー克高度的setter,没有找到调整展开高度的设置方法。
public class DialogMediaDetails extends BottomSheetDialogFragment
{
    @Override
    public void setupDialog(Dialog dialog, int style)
    {
        super.setupDialog(dialog, style);
        View view = View.inflate(getContext(), R.layout.dialog_media_details, null);
        dialog.setContentView(view);

        ...

        View bottomSheet = dialog.findViewById(R.id.design_bottom_sheet);
        BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
        behavior.setPeekHeight(...);
        // how to set maximum expanded height???? Or a minimum top offset?

    }
}

编辑

为什么需要这样做?因为我在全屏活动中展示了一个 BottomSheet 对话框,如果 BottomSheet 在顶部留下空间,那么它看起来很糟糕...


3
我想知道如何设置“底部弹出式对话框”的“最大高度”。我需要我的底部弹出式对话框只占据屏幕的一半,即使拖动也不能超过屏幕顶部...有什么建议吗? - eRaisedToX
1
@abat 希望这可以帮到你:https://dev59.com/wVsV5IYBdhLWcg3w6yYh#53791225 - Pratik Butani
@eRaisedToX 你也可以尝试一下:https://dev59.com/wVsV5IYBdhLWcg3w6yYh#53791225 - Pratik Butani
7个回答

48
由于膨胀的视图被添加到具有layout_height = wrap_content的FrameLayout中,因此高度被包裹。请参见https://github.com/dandar3/android-support-design/blob/master/res/layout/design_bottom_sheet_dialog.xml上的FrameLayout(R.id.design_bottom_sheet)。下面的类可使底部表单全屏,背景透明,并完全展开到顶部。
public class FullScreenBottomSheetDialogFragment extends BottomSheetDialogFragment {


    @CallSuper
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ButterKnife.bind(this, view);
    }


    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();

        if (dialog != null) {
            View bottomSheet = dialog.findViewById(R.id.design_bottom_sheet);
            bottomSheet.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
        }
        View view = getView();
        view.post(() -> {
            View parent = (View) view.getParent();
            CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) (parent).getLayoutParams();
            CoordinatorLayout.Behavior behavior = params.getBehavior();
            BottomSheetBehavior bottomSheetBehavior = (BottomSheetBehavior) behavior;
            bottomSheetBehavior.setPeekHeight(view.getMeasuredHeight());
            ((View)bottomSheet.getParent()).setBackgroundColor(Color.TRANSPARENT)

        });
    }

}

--- 2018年8月30日编辑 --- 一年后我意识到背景色错放在了错误的视图上。这会导致当用户拖动对话框时,背景色也会随之移动。 我已经修复了这个问题,使得底部表的父视图被着色。


1
使用androidX,可以这样写: dialog?.findViewById<View>( com.google.android.material.R.id.design_bottom_sheet ) - hmac
1
有人能解释为什么在 onCreateDialogonViewCreated 中,查找底部表视图似乎无法正常工作吗? - hmac
这是我要找的内容:View bottomSheet = dialog.findViewById(R.id.design_bottom_sheet); bottomSheet.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; - Shailendra Madda

37

我发现了一个更简单的答案;在你的例子中,通过以下代码获取底部表单的 FrameLayout

View bottomSheet = dialog.findViewById(R.id.design_bottom_sheet);

然后,您可以在该视图的布局参数上设置高度,以将其展开高度设置为所需高度。

bottomSheet.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;

6
使用AndroidX,使用以下代码来获取底部菜单对应的View: dialog?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) - hmac
我注意到的一件事是,只有当我们底部表单的父布局是RelativeLayout时,这才有效。 - akashzincle

26

BIG UPDATE 避免重复代码,我提供了一个链接到完整答案的地方,你可以在那里找到有关如何获得像 Google 地图一样的完整行为的所有解释。


我想调整它的最大展开高度。我该怎么做?

BottomSheetBottomSheetDialogFragment都使用Support Library 23.x中可找到的BottomSheetBehavior。该 Java 类具有两种不同用途的 mMinOffset,其中一个用于定义它将用于绘制内容的父级区域(可能是 NestedScrollView)。另一种用法是定义扩展锚点,也就是说,如果您将其向上滑动以形成STATE_COLLAPSED,它将动画显示您的 BottomSheet直到达到此锚点,但如果您仍然保持向上滑动以覆盖父级高度(CoordiantorLayout Height),则会继续展开。

如果您查看 BottomSheetDialog,则会看到此方法:

private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
    final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(),
            android.support.design.R.layout.design_bottom_sheet_dialog, null);
    if (layoutResId != 0 && view == null) {
        view = getLayoutInflater().inflate(layoutResId, coordinator, false);
    }
    FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(android.support.design.R.id.design_bottom_sheet);
    BottomSheetBehavior.from(bottomSheet).setBottomSheetCallback(mBottomSheetCallback);
    if (params == null) {
        bottomSheet.addView(view);
    } else {
        bottomSheet.addView(view, params);
    }
    // We treat the CoordinatorLayout as outside the dialog though it is technically inside
    if (shouldWindowCloseOnTouchOutside()) {
        final View finalView = view;
        coordinator.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (isShowing() &&
                        MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_UP &&
                        !coordinator.isPointInChildBounds(finalView,
                                (int) event.getX(), (int) event.getY())) {
                    cancel();
                    return true;
                }
                return false;
            }
        });
    }
    return coordinator;
}



不知道你需要哪种行为,但如果你需要第二种,请按照以下步骤进行:

  1. Create a Java class and extend it from CoordinatorLayout.Behavior<V>

  2. Copy paste code from the default BottomSheetBehavior file to your new one.

  3. Modify the method clampViewPositionVertical with the following code:

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
        return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
    }
    int constrain(int amount, int low, int high) {
        return amount < low ? low : (amount > high ? high : amount);
    }
    
  4. Add a new state

    public static final int STATE_ANCHOR_POINT = X;
    
  5. Modify the next methods: onLayoutChild, onStopNestedScroll, BottomSheetBehavior<V> from(V view) and setState (optional)

以下是它的外观
[CustomBottomSheetBehavior]


23

对我有用。在BottomSheetDialogFragment的onViewCreated()方法中添加代码。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {

            view.viewTreeObserver.removeOnGlobalLayoutListener(this)

            val dialog = dialog as BottomSheetDialog
            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

            val newHeight = activity?.window?.decorView?.measuredHeight
            val viewGroupLayoutParams = bottomSheet.layoutParams
            viewGroupLayoutParams.height = newHeight ?: 0
            bottomSheet.layoutParams = viewGroupLayoutParams
        }
    })
    dialogView = view
}

不要忘记移除 viewTreeObserver。

override fun onDestroyView() {
    dialogView?.viewTreeObserver?.addOnGlobalLayoutListener(null)
    super.onDestroyView()
}

使用 addOnGlobalLayoutListener 会导致屏幕闪烁,主要逻辑可以放在 onViewCreated 中合法执行。 - Owen Chen
我注意到decorview也考虑了导航视图的高度,因此任何底部对齐父视图的视图在替换activity?.window?.decorView?.measuredHeight为使用displaymetrics获取的像素高度后,在底部被裁剪。 - akashzincle

12

获取工作表行为的引用

private val behavior by lazy { (dialog as BottomSheetDialog).behavior }

关闭fitToContents并将expandedOffset设置为所需像素。

behavior.isFitToContents = false
behavior.expandedOffset = 100

5

我建议不要使用id来查找视图。在 BottomSheetDialogFragment 中,对话框是一个 BottomSheetDialog ,它公开了底部工作表的行为。您可以使用它来设置 peek 高度。

(dialog as BottomSheetDialog).behavior.peekHeight = ...

5

Kotlin

在我的情况下,我需要定义一个固定的高度,我做了以下操作:

val bottomSheet: View? = dialog.findViewById(R.id.design_bottom_sheet)
BottomSheetBehavior.from(bottomSheet!!).peekHeight = 250

这样您就可以访问BottomSheetBehavior的任何属性,例如halfExpandedRatio


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