如何使用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)。请注意,您无法在视图布局之前调用该方法。
suggested practice要求视图先膨胀,但我不确定如何将BottomSheetBehavi设置为片段(BottomSheetDialogFragment
)。
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
答案 0 :(得分:123)
"请注意,您无法在视图布局之前调用该方法。"
以上文字为线索。
对话框有一个侦听器,一旦对话框显示就会触发。如果没有布局,则无法显示该对话框。
因此,在模态底部工作表的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
除了返回BottomSheetDialog
之外什么都不做。
更新ANDROIDX
使用AndroidX时,现在可以在android.support.design.R.id.design_bottom_sheet
找到以前在com.google.android.material.R.id.design_bottom_sheet
找到的资源。
答案 1 :(得分:20)
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);
}
答案 2 :(得分:3)
在OnResume中应用BottomsheetDialogFragment状态将解决此问题
@Override
public void onResume() {
super.onResume();
if(mBehavior!=null)
mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
onShow(DialogInterface对话框)和postDelayed可能会导致动画故障
答案 3 :(得分:2)
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。
以下是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了。
答案 4 :(得分:2)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).apply {
setOnShowListener {
(this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState(
BottomSheetBehavior.STATE_EXPANDED
)
}
}
}
答案 5 :(得分:1)
使用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将所有成员都设为私有。但它解决了渲染bug。 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());
}
....
}
答案 6 :(得分:1)
我实施的最简单方法如下,在这里我们找到 android.support.design.R.id.design_bottom_sheet 并将底页状态设置为 EXPANDED 。< / p>
如果没有这个,我的底页总是处于COLLAPSED状态,如果视图高度超过屏幕高度的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
}
}
答案 7 :(得分:1)
我认为以上这些更好。 可悲的是,在解决之前我没有找到那些解决方案。 但是写我的解决方案。与所有人都很相似。
================================================ =================================
我面临同样的问题。 这就是我解决的问题。 行为隐藏在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()
}
}
答案 8 :(得分: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"
也可以参考材料设计参考Bottom Sheets
答案 9 :(得分:1)
我的答案与上述大多数答案大致相同,只是稍作修改。我宁愿不要对任何框架视图资源ID进行硬编码,而不是先使用findViewById查找底部工作表视图,因为它们将来可能会更改。
setOnShowListener(dialog -> {
BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior();
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
答案 10 :(得分:1)
将此内容发布给将来的读者,因为我认为现在我们可以使用其他解决方案。
我正在尝试解决与BottomSheetDialog
描述的问题相同的问题。
我不喜欢使用内部Android ID,我刚刚发现BottomSheetDialog
getBehavior
内有一个可以使用的方法:
您可以在BottomSheetDialog
中使用它:
behavior.state = BottomSheetBehavior.STATE_EXPANDED
使用BottomSheetDialogFragment
,您可以执行同样的操作,将对话框从DialogFragment投射到BottomSheetDialog
。
答案 11 :(得分: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
}
答案 12 :(得分:1)
在 Kotlin 中,在 onStart()
BottomSheetDialogFragment
中添加以下行
(dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
答案 13 :(得分: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
}
答案 14 :(得分:-1)
根据以下链接的回复,这对我有用。
behavior = BottomSheetBehavior.from(bottomSheet1);
if(action.equals("post") ) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
behavior.setDraggable(false); // disable dragging
}