将 BottomSheetDialogFragment 的状态设置为展开
Set state of BottomSheetDialogFragment to expanded
如何使用 Android 支持设计库 (v23.2.1) 将扩展 BottomSheetDialogFragment
的片段状态设置为使用 BottomSheetBehavior#setState(STATE_EXPANDED)
扩展?
https://code.google.com/p/android/issues/detail?id=202396 说:
Bottom sheets are set to STATE_COLLAPSED at first. Call BottomSheetBehavior#setState(STATE_EXPANDED) if you want to expand it. Note that you cannot call the method before view layouts.
suggested practice 需要先膨胀视图,但我不确定如何将 BottomSheetBehaviour 设置到片段上 (BottomSheetDialogFragment
)。
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
"Note that you cannot call the method before view layouts."
以上文字就是线索
对话框有一个侦听器,一旦对话框显示就会被触发。对话框不布局就无法显示
所以,在你的模式底部 sheet (BottomSheetFragment
) 的 onCreateDialog()
中,就在 return 对话框之前(或任何地方,一旦你有参考对话框),调用:
// 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
除了 return a BottomSheetDialog
.[=22= 什么都不做]
AndroidX更新
使用 AndroidX 时,以前在 android.support.design.R.id.design_bottom_sheet
找到的资源现在可以在 com.google.android.material.R.id.design_bottom_sheet
找到。
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);
}
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)
returns null.
很奇怪。我将这行代码添加到 Android Monitor in DEBUG 模式下的 Watches 中,发现 return Framelayout 正常。
这是 BottomSheetDialog 中 wrapInBottomSheet
的代码:
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() 的所有结果都会导致随机渲染错误。请参见下面的屏幕截图 - BottomSheet 对话框不在屏幕底部,而是像显示键盘一样放置。这个问题并不总是发生,但经常发生。
更新
我的私有成员反射的解决方案是不必要的。使用 postDelayed(大约 100 毫秒)在隐藏软键盘后创建和显示对话框是一个更好的解决方案。那么上面用onShow()的方案就ok了
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 class 只是具有创建 BottomSheetDialog 的 onCreateDialog 方法的 AppCompatDialogFragment。我创建自己的 AppCompatDialogFragment 子项,它创建我的 class extends 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
可以子classed 来解决这个问题:
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;
}
}
因此扩展此 class 而不是 BottomSheetDialogFragment
以创建您自己的底部 sheet。
备注
如果您的项目使用旧的 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 并将底部 sheet 状态设置为 EXPANDED.
没有这个,我的底部 sheet 总是卡在折叠状态 如果视图高度超过屏幕高度的 0.5,我必须手动滚动才能查看全底 sheet.
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
}
}
我觉得上面那些比较好。
可悲的是,在我解决之前我没有找到那些解决方案。
但是写下我的解决方案。与所有人都非常相似。
============================================= =====================================
我遇到了同样的问题。
这就是我解决的。
Behavior 隐藏在 BottomSheetDialog 中,可以通过它来获取 behavior
如果您不想将父布局更改为 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 中,您可以声明从 [=11= 延伸的片段 class ]],创建视图时可以在显示对话框后设置对话框侦听器默认状态。
STATE_COLLAPSED: The bottom sheet is visible but only showing its peek
height.
STATE_EXPANDED: The bottom sheet is visible and its maximum height.
STATE_HALF_EXPANDED: The bottom sheet is visible but only showing its
half height.
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 中使用 material 设计实现。
implementation "com.google.android.material:material:$version"
另请参阅 material 设计参考 Bottom Sheets
我的回答与上面的大部分回答大致相同,只是稍作修改。与其使用 findViewById 首先找到底部 sheet 视图,我更倾向于不对任何框架视图资源 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
。
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
}
在您的 Kotlin BottomSheetDialogFragment class 中,如下重写 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 中,在 BottomSheetDialogFragment
的 onStart()
中添加以下行
(dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
根据下面 link 的回复,这对我有用。
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 中扩展 BottomSheet 视图的简单解决方案:
(dialog as BottomSheetDialog).behavior.state =
BottomSheetBehavior.STATE_EXPANDED
如何使用 Android 支持设计库 (v23.2.1) 将扩展 BottomSheetDialogFragment
的片段状态设置为使用 BottomSheetBehavior#setState(STATE_EXPANDED)
扩展?
https://code.google.com/p/android/issues/detail?id=202396 说:
Bottom sheets are set to STATE_COLLAPSED at first. Call BottomSheetBehavior#setState(STATE_EXPANDED) if you want to expand it. Note that you cannot call the method before view layouts.
suggested practice 需要先膨胀视图,但我不确定如何将 BottomSheetBehaviour 设置到片段上 (BottomSheetDialogFragment
)。
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
"Note that you cannot call the method before view layouts."
以上文字就是线索
对话框有一个侦听器,一旦对话框显示就会被触发。对话框不布局就无法显示
所以,在你的模式底部 sheet (BottomSheetFragment
) 的 onCreateDialog()
中,就在 return 对话框之前(或任何地方,一旦你有参考对话框),调用:
// 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
除了 return a BottomSheetDialog
.[=22= 什么都不做]
AndroidX更新
使用 AndroidX 时,以前在 android.support.design.R.id.design_bottom_sheet
找到的资源现在可以在 com.google.android.material.R.id.design_bottom_sheet
找到。
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);
}
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)
returns null.
很奇怪。我将这行代码添加到 Android Monitor in DEBUG 模式下的 Watches 中,发现 return Framelayout 正常。
这是 BottomSheetDialog 中 wrapInBottomSheet
的代码:
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() 的所有结果都会导致随机渲染错误。请参见下面的屏幕截图 - BottomSheet 对话框不在屏幕底部,而是像显示键盘一样放置。这个问题并不总是发生,但经常发生。
更新
我的私有成员反射的解决方案是不必要的。使用 postDelayed(大约 100 毫秒)在隐藏软键盘后创建和显示对话框是一个更好的解决方案。那么上面用onShow()的方案就ok了
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 class 只是具有创建 BottomSheetDialog 的 onCreateDialog 方法的 AppCompatDialogFragment。我创建自己的 AppCompatDialogFragment 子项,它创建我的 class extends 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
可以子classed 来解决这个问题:
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;
}
}
因此扩展此 class 而不是 BottomSheetDialogFragment
以创建您自己的底部 sheet。
备注
如果您的项目使用旧的 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 并将底部 sheet 状态设置为 EXPANDED.
没有这个,我的底部 sheet 总是卡在折叠状态 如果视图高度超过屏幕高度的 0.5,我必须手动滚动才能查看全底 sheet.
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
}
}
我觉得上面那些比较好。 可悲的是,在我解决之前我没有找到那些解决方案。 但是写下我的解决方案。与所有人都非常相似。
============================================= =====================================
我遇到了同样的问题。 这就是我解决的。 Behavior 隐藏在 BottomSheetDialog 中,可以通过它来获取 behavior 如果您不想将父布局更改为 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 中,您可以声明从 [=11= 延伸的片段 class ]],创建视图时可以在显示对话框后设置对话框侦听器默认状态。
STATE_COLLAPSED: The bottom sheet is visible but only showing its peek height.
STATE_EXPANDED: The bottom sheet is visible and its maximum height.
STATE_HALF_EXPANDED: The bottom sheet is visible but only showing its half height.
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 中使用 material 设计实现。
implementation "com.google.android.material:material:$version"
另请参阅 material 设计参考 Bottom Sheets
我的回答与上面的大部分回答大致相同,只是稍作修改。与其使用 findViewById 首先找到底部 sheet 视图,我更倾向于不对任何框架视图资源 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
。
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
}
在您的 Kotlin BottomSheetDialogFragment class 中,如下重写 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 中,在 BottomSheetDialogFragment
onStart()
中添加以下行
(dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
根据下面 link 的回复,这对我有用。
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 中扩展 BottomSheet 视图的简单解决方案:
(dialog as BottomSheetDialog).behavior.state =
BottomSheetBehavior.STATE_EXPANDED