将BottomSheetDialogFragment的状态设置为展开


92

如何使用Android支持设计库(v23.2.1)设置扩展BottomSheetDialogFragment为扩展的片段的状态BottomSheetBehavior#setState(STATE_EXPANDED)

https://code.google.com/p/android/issues/detail?id=202396说:

首先将底页设置为STATE_COLLAPSED。如果要扩展它,请调用BottomSheetBehavior#setState(STATE_EXPANDED)。请注意,您不能在视图布局之前调用该方法。

建议的做法需要将第一膨胀的观点,但我不知道我怎么会设置BottomSheetBehaviour到片段(BottomSheetDialogFragment)。

View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);  
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);  

Answers:


221

“请注意,您不能在视图布局之前调用该方法。”

以上文字就是线索。

对话框曾经是对话框被开了听众所示。如果未布置对话框,则无法显示。

因此,在onCreateDialog()模态底部工作表(BottomSheetFragment)的中,就在返回对话框之前(或在有了对话框的引用之后,在任何地方),调用:

// This listener's onShow is fired when the dialog is shown
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
    @Override
    public void onShow(DialogInterface dialog) {

        // In a previous life I used this method to get handles to the positive and negative buttons
        // of a dialog in order to change their Typeface. Good ol' days.

        BottomSheetDialog d = (BottomSheetDialog) dialog;

        // This is gotten directly from the source of BottomSheetDialog
        // in the wrapInBottomSheet() method
        FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);

        // Right here!
        BottomSheetBehavior.from(bottomSheet)
            .setState(BottomSheetBehavior.STATE_EXPANDED);
    }
});

就我而言,我的习惯BottomSheet是:

@SuppressWarnings("ConstantConditions")
public class ShareBottomSheetFragment extends AppCompatDialogFragment {

    @NonNull @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        BottomSheetDialog dialog =
                new BottomSheetDialog(getActivity(), R.style.Haute_Dialog_ShareImage);

        dialog.setContentView(R.layout.dialog_share_image);

        dialog.findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

        SwitchCompat switchview = (SwitchCompat) dialog.findViewById(R.id.switchview);
        switchview.setTypeface(FontCache.get(dialog.getContext(), lookup(muli, NORMAL)));

        return dialog;
    }
}

让我知道是否有帮助。

更新

请注意,您还可以覆盖BottomSheetDialogFragment为:

public class SimpleInitiallyExpandedBottomSheetFragment extends BottomSheetDialogFragment {

    @NonNull @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

        // Do something with your dialog like setContentView() or whatever
        return dialog;
    }
}

但是我真的不明白为什么任何人都想这么做,因为基地BottomSheetFragment除了返回a之外什么都不做BottomSheetDialog

更新ANDROIDX

使用AndroidX时,android.support.design.R.id.design_bottom_sheet现在可以在找到资源com.google.android.material.R.id.design_bottom_sheet


谢谢,我尝试了这种方法。BottomSheetDialogFragment从折叠行为扩展到展开行为时,它使外观显得混乱(在打开的动画中似乎跳过帧)。编辑:在Android棉花糖和KitKat设备上测试了此内容
user2560886 2016年

1
它非常适合我。不跳过。除了返回对话框外,您还在做其他事情吗?如果您用代码更新您的帖子,将不胜感激,这样我可以有一个更好的主意。
efemoney

5
android.support.design.R更新支持库后我找不到包吗?
natario

2
我也遇到了解决问题android.support.design.R,就像@natario一样。我正在使用implementation "com.google.android.material:material:1.0.0"。我还在项目中使用AndroidX。
hsson

21
使用AndroidX时,资源可在com.google.android.material.R.id.design_bottom_sheet
Emergencyx'Nov 23''18

45

efeturi的答案很好,但是,如果要使用onCreateView()创建BottomSheet,而不是使用onCreateDialog(),则需要在onCreateView()方法下添加以下代码:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            View bottomSheetInternal = d.findViewById(android.support.design.R.id.design_bottom_sheet);
            BottomSheetBehavior.from(bottomSheetInternal).setState(BottomSheetBehavior.STATE_EXPANDED);
        }
    });
    return inflater.inflate(R.layout.your_bottomsheet_content_layout, container, false);
}

3
另外,您根本不需要调用getDialog。我发现最干净的方法是同时覆盖onCreateView和onCreateDialog。建立在onCreateView视图(你会与任何片段),并做onCreateDialog对话特定代码(调用super.onCreateDialog来获得实例)
Stimsoni

2
这救了我的一天。谢谢。
AndroidRuntimeException

如果使用onCreateDialog,则不会调用@Stimsoni onCreateView。 developer.android.com/reference/android/support/v4/app/…–
Vincent_Paing

1
@Vincent_Paing,是的。在您的附加链接中说“不需要实现onCreateView”。它没有说它不会被调用。在github.com/material-components/material-components-android/blob/…处查看源代码。默认实现调用onCreateDialog来创建底部工作表,并且上面的每个解决方案仍在使用onCreateView,这意味着它们总是被调用。只要确保您仍然重写super.onCreateDialog()即可。
Stimsoni

在BottomSheetDialogFragment中,它使我在onCreateView()中崩溃,我将其放在onViewCreated()中,这是完美的!谢谢
avisper

22

一个简单而优雅的解决方案:

BottomSheetDialogFragment 可以子类化以解决此问题:

class NonCollapsableBottomSheetDialogFragment extends BottomSheetDialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);

        bottomSheetDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                FrameLayout bottomSheet = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);

                BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
                behavior.setSkipCollapsed(true);
                behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });
        return bottomSheetDialog;
    }
}

因此,扩展此类而不是BottomSheetDialogFragment创建自己的底页。

注意

更改com.google.android.material.R.id.design_bottom_sheetandroid.support.design.R.id.design_bottom_sheet项目是否使用旧的Android支持库。


1
似乎是com.google.android.material.R现在而不是android.support.design.R
EpicPandaForce

@EpicPandaForce当然。Google的Android团队最近重新打包了以前的支​​持库。
DYS

4

我认为以上这些更好。可悲的是,在解决之前我没有找到那些解决方案。但是写我的解决方案。十分相似。

=================================================== ===============================

我面临着同样的问题。这就是我解决的问题。行为隐藏在BottomSheetDialog中,该行为可用于获取行为。如果您不想将父布局更改为CooridateLayout,则可以尝试此操作。

步骤1:自定义BottomSheetDialogFragment

open class CBottomSheetDialogFragment : BottomSheetDialogFragment() {
   //wanna get the bottomSheetDialog
   protected lateinit var dialog : BottomSheetDialog 
   override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
      dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
      return dialog
   }

   //set the behavior here
   fun setFullScreen(){
      dialog.behavior.state = STATE_EXPANDED
   }
}

步骤2:使您的片段扩展此自定义片段

class YourBottomSheetFragment : CBottomSheetDialogFragment(){

   //make sure invoke this method after view is built
   //such as after OnActivityCreated(savedInstanceState: Bundle?)
   override fun onStart() {
      super.onStart()
      setFullScreen()//initiated at onActivityCreated(), onStart()
   }
}

3
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

我遇到NullPointException是BottomSheetBehavior.from(bottomSheet)因为d.findViewById(android.support.design.R.id.design_bottom_sheet)返回null。

真奇怪。我在DEBUG模式下将这行代码添加到Android Monitor中的Watches,发现它正常返回Framelayout。

这是wrapInBottomSheetBottomSheetDialog中的代码:

private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
        final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(),
                R.layout.design_bottom_sheet_dialog, null);
        if (layoutResId != 0 && view == null) {
            view = getLayoutInflater().inflate(layoutResId, coordinator, false);
        }
        FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(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()) {
            coordinator.findViewById(R.id.touch_outside).setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            if (isShowing()) {
                                cancel();
                            }
                        }
                    });
        }
        return coordinator;
    }

有时,我发现R.id.design_bottom_sheet不等于android.support.design.R.id.design_bottom_sheet。它们在不同的R.java中具有不同的值。

因此,我更改android.support.design.R.id.design_bottom_sheetR.id.design_bottom_sheet

dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(R.id.design_bottom_sheet); // use R.java of current project
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

现在不再有NullPointException。


3

应用BottomsheetDialogFragment状态onResume将解决此问题

@Override
public void onResume() {
    super.onResume();
    if(mBehavior!=null)
       mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}

onShow(DialogInterface dialog)postDelayed可能导致动画故障


2

当显示软键盘时,使用onShow()的所有结果都会导致随机渲染错误。参见下面的屏幕快照-BottomSheet对话框不在屏幕底部,但其放置方式类似于显示键盘。这个问题并非总是发生,而是经常发生。

在此处输入图片说明

更新

我的反映私有成员的解决方案是不必要的。隐藏软键盘后使用postDelayed(大约100毫秒)创建和显示对话框是更好的解决方案。然后使用onShow()的上述解决方案就可以了。

Utils.hideSoftKeyboard(this);
mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        MyBottomSheetDialog dialog = new MyBottomSheetDialog();
        dialog.setListener(MyActivity.this);
        dialog.show(getSupportFragmentManager(), TAG_BOTTOM_SHEET_DLG);
    }
}, 100);

因此,我实现了其他解决方案,但是它需要使用反射,因为BottomSheetDialog将所有成员都设为私有。但这解决了渲染错误。BottomSheetDialogFragment类仅是具有创建BottomSheetDialog的onCreateDialog方法的AppCompatDialogFragment。我创建了自己的AppCompatDialogFragment子级,该类创建了我的类,扩展了BottomSheetDialog并解决了对私有行为成员的访问,并将其在onStart方法中设置为STATE_EXPANDED状态。

public class ExpandedBottomSheetDialog extends BottomSheetDialog {

    protected BottomSheetBehavior<FrameLayout> mBehavior;

    public ExpandedBottomSheetDialog(@NonNull Context context, @StyleRes int theme) {
        super(context, theme);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            Field privateField = BottomSheetDialog.class.getDeclaredField("mBehavior");
            privateField.setAccessible(true);
            mBehavior = (BottomSheetBehavior<FrameLayout>) privateField.get(this);
        } catch (NoSuchFieldException e) {
            // do nothing
        } catch (IllegalAccessException e) {
            // do nothing
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mBehavior != null) {
            mBehavior.setSkipCollapsed(true);
            mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }
    }
}


public class AddAttachmentBottomSheetDialog extends AppCompatDialogFragment {

    ....

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new ExpandedBottomSheetDialog(getContext(), getTheme());
    }

    ....
}

1

我实现的最简单方法如下,在这里我们找到android.support.design.R.id.design_bottom_sheet并将底部工作表状态设置为EXPANDED

如果没有此设置,如果视图高度大于屏幕高度的0.5 ,则我的底板总是卡在COLLAPSED状态,并且我必须手动滚动才能查看整个底板。

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

    private lateinit var mBehavior: BottomSheetBehavior<FrameLayout>

    override fun setContentView(view: View) {
        super.setContentView(view)
        val bottomSheet = window.decorView.findViewById<View>(android.support.design.R.id.design_bottom_sheet) as FrameLayout
        mBehavior = BottomSheetBehavior.from(bottomSheet)
        mBehavior.state = BottomSheetBehavior.STATE_EXPANDED
    }

    override fun onStart() {
        super.onStart()
        mBehavior.state = BottomSheetBehavior.STATE_EXPANDED
    }
}

1

uregentx答案类似,在kotlin中,您可以声明从扩展的片段类BottomSheetDialogFragment,并且在创建视图时,可以在显示对话框之后设置对话框侦听器的默认状态。

STATE_COLLAPSED:底部可见,但仅显示其窥视高度。

STATE_EXPANDED:底页是可见的,并且它的最大高度。

STATE_HALF_EXPANDED:底部可见,但仅显示其一半高度。

class FragmentCreateGroup : BottomSheetDialogFragment() {
      ...

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
        // Set dialog initial state when shown
        dialog?.setOnShowListener {
            val bottomSheetDialog = it as BottomSheetDialog
            val sheetInternal: View = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet)!!
            BottomSheetBehavior.from(sheetInternal).state = BottomSheetBehavior.STATE_COLLAPSED
        }

        val view = inflater.inflate(R.layout.fragment_create_group, container, false)
        ...

        return view
    }
}

记住在gradle中使用材料设计实现。

implementation "com.google.android.material:material:$version"

另请参阅材料设计参考底页


哪里的dialog?变量来自于onCreateView?
埃里克·史密斯

dialog是该类的一个属性DialogFragment,实际上是一个Getter。在此示例中,我使用该吸气剂来获取当前的DialogFragment实例并将setOnShowListener其获取。可能是您已经在项目中(例如在活动中)使用了此类指令,以便使用操作栏actionBargetter,因此您可以修改该组件,例如actionBar?.subtitle = "abcd"
FJCG

1

我的答案与上述大多数答案大致相同,但略有修改。我宁愿不要对任何框架视图资源ID进行硬编码,而不是先使用findViewById查找底部工作表视图,因为它们将来可能会更改。

setOnShowListener(dialog -> {
            BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior();
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        });

1
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return super.onCreateDialog(savedInstanceState).apply {
        setOnShowListener {
            (this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState(
                BottomSheetBehavior.STATE_EXPANDED
            )
        }
    }
}

1

据我认为,我们可以在这里将它发布给将来的读者,我们可以使用其他解决方案。

我正在尝试解决与您描述的相同的问题BottomSheetDialog

我不喜欢使用内部的Android ID,我发现里面有一种BottomSheetDialog getBehavior可以使用的方法:

您可以在您的内部使用它BottomSheetDialog

behavior.state = BottomSheetBehavior.STATE_EXPANDED

使用,BottomSheetDialogFragment您可以执行相同的操作,将对话框从DialogFragment投射到BottomSheetDialog


1

BottomSheetDialogFragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED
}

或准备显示时:

private fun onContentLoaded(items: List<Any>) {
    adapter.submitList(items)
    (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED
}

0

在您的Kotlin BottomSheetDialogFragment类中,按如下所示覆盖onCreateDialog

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
        bottomSheetDialog.setOnShowListener {
            val bottomSheet =
                bottomSheetDialog.findViewById<FrameLayout>(
                    com.google.android.material.R.id.design_bottom_sheet
                )
            val behavior = BottomSheetBehavior.from(bottomSheet!!)
            behavior.skipCollapsed = true
            behavior.state = BottomSheetBehavior.STATE_EXPANDED
        }
        return bottomSheetDialog
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.