将 BottomSheetDialogFragment 的状态设置为展开

如何使用 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)。请注意,不能在视图布局之前调用该方法。

建议的做法要求首先对视图进行充气,但是我不确定如何将 BottomSheetBehavior 设置为片段(BottomSheetDialogFragment)。

View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
126507 次浏览

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

以上文字为线索。

对话框有一个侦听器,一旦对话框为 显示,侦听器就会被激发。如果没有布局,则无法显示对话框。

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

// 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除了返回一个 BottomSheetDialog之外什么也不做。

安卓系统更新

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

Efeturi 的答案很棒,但是,如果你想使用 < em > 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);
}
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);
}
});

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

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

下面是 wrapInBottomSheet在 BottomSheetDialog 中的代码:

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_sheet改成 R.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。

当软键盘显示时,使用 onShow ()的所有结果都会导致随机渲染错误。参见下面的截图-底部的对话框不在屏幕的底部,而是像显示键盘一样放置。这个问题并不总是发生,但经常发生。

enter image description here

更新

我的解决方案与私人成员的反映是不必要的。在隐藏软键盘之后,使用 postDelayed (大约100ms)创建和显示对话框是一个更好的解决方案。那么上面的解决方案与 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 类只是具有 onCreateDialog 方法的 AppCompatDialogFragment,该方法创建 BottomSheetDialog。我创建了我自己的 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());
}


....
}

onResume中应用 BottomsheetDialogFragment状态可以解决这个问题

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

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

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

为了解决这个问题,可以对 BottomSheetDialogFragment进行子类化:

class NonCollapsableBottomSheetDialogFragment extends BottomSheetDialogFragment {


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


dialog.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 dialog;
}
}

因此,扩展这个类而不是 BottomSheetDialogFragment来创建您自己的底层工作表。

注意

如果您的项目使用旧的 Android 支持库,请将 com.google.android.material.R.id.design_bottom_sheet更改为 android.support.design.R.id.design_bottom_sheet

最简单的方法我实现如下,这里我们找到 Android.support.design.R.id.design _ bottom _ sheet和设置底部表状态为 扩大

如果没有这个,我的底部工作表 总是陷入崩溃的状态如果查看高度超过0.5屏幕高度,我必须手动滚动查看完整的底部工作表。

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
}
}

我觉得上面那些更好。 遗憾的是,我没有找到这些解决方案之前,我已经解决了。 但是写我的解决方案。非常相似的所有。

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

我也面临同样的问题。 这就是我解决的问题。 行为隐藏在 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 = BottomSheetBehavior.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()
}
}

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

STATE _ COLLAPSED: 底部的工作表是可见的,但只显示它的一瞥 身高。

状态 _ 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
}
}

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

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

还要看看材料设计参考 底层床单

我的回答与上述大部分答案大致相同,只是稍作修改。与其使用 findViewById 首先查找底部工作表视图,我更倾向于不硬编码任何框架视图资源 id,因为它们在将来可能会更改。

setOnShowListener(dialog -> {
BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior();
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).apply {
setOnShowListener {
(this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState(
BottomSheetBehavior.STATE_EXPANDED
)
}
}
}

把这个贴在这里给未来的读者,因为我认为现在我们可以使用另一种解决方案。

我想解决的问题和你用 BottomSheetDialog解决的一样。

我不喜欢使用内部的 Android id,我刚刚发现在 BottomSheetDialog getBehavior中有一个方法你可以使用:

你可以在你的 BottomSheetDialog里面使用这个:

behavior.state = BottomSheetBehavior.STATE_EXPANDED

使用 BottomSheetDialogFragment,您可以将对话框从 DialogFragment 转换为 BottomSheetDialog

返回文章页面底层工作表对话框译者:

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
}

在 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
}

在 Kotlin,在你的 abc1的 abc0中添加下面的行

(dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED

根据下面链接的回应,这对我来说很有效。

behavior = BottomSheetBehavior.from(bottomSheet1);
if(action.equals("post") ) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
behavior.setDraggable(false); // disable dragging
}

在这里输入链接描述

你可以这样做(Kotlin 版本) :

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
}

这里有一个非常简洁的 Kotlin 解决方案,非常有效。

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return (super.onCreateDialog(savedInstanceState) as BottomSheetDialog).apply {
setOnShowListener {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
}

扩展 Kotlin 底层视图的简单方法:

(dialog as BottomSheetDialog).behavior.state =
BottomSheetBehavior.STATE_EXPANDED

简单的回答(Kotlin + 片段 + bottomSheetDialogViewBinding) :

val bsd = BottomSheetDialog(requireContext())
val bsdBinding = DialogBottomSheetViewBinding.inflate(LayoutInflater.from(requireContext()))
bsd.behavior.setState(BottomSheetBehavior.STATE_EXPANDED);