将BottomSheetDialogFragment的状态设置为展开

时间:2016-03-11 10:16:04

标签: android android-fragments android-support-library android-support-design

如何使用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);  

15 个答案:

答案 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对话框不在屏幕底部,但显示的位置与键盘一样。这个问题不会经常发生,但经常发生。

enter image description here

<强>更新

我不需要反映私人成员的解决方案。隐藏软键盘后使用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
}

enter link description here