«Обратите внимание, что вы не можете вызвать метод перед просмотром макетов».
Приведенный выше текст является подсказкой.
У диалогов есть слушатель, который запускается после показа диалога . Диалог не может быть показан, если он не выложен.
Итак, в onCreateDialog()
вашем модальном нижнем листе ( BottomSheetFragment
), непосредственно перед возвратом диалогового окна (или где угодно, если у вас есть ссылка на диалоговое окно), вызовите:
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);
}
});
В моем случае мой обычай 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);
}
});
return dialog;
}
}
Но я действительно не понимаю, почему кто-то захочет это сделать, поскольку база BottomSheetFragment
не делает ничего, кроме возврата BottomSheetDialog
.
ОБНОВЛЕНИЕ ДЛЯ ANDROIDX
При использовании AndroidX ресурс, ранее находившийся по адресу, android.support.design.R.id.design_bottom_sheet
теперь можно найти по адресу com.google.android.material.R.id.design_bottom_sheet
.
BottomSheetDialogFragment
дерганным (кажется, пропускает кадры в начальной анимации) при переходе от свернутого к расширенному поведению. Изменить: протестировано на устройствах Android Marshmallow и KitKatandroid.support.design.R
после обновления библиотек поддержки?android.support.design.R
, как и у @natario. Я используюimplementation "com.google.android.material:material:1.0.0"
. Еще я использую в проекте AndroidX.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); }
источник
Простое и элегантное решение:
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_sheet
на,android.support.design.R.id.design_bottom_sheet
если в вашем проекте используются старые библиотеки поддержки Android.источник
com.google.android.material.R
сейчас вместоandroid.support.design.R
.Я думаю, что те, что указаны выше, лучше. К сожалению, я не нашел этого решения до того, как решил. Но напишите мое решение. очень похоже на все.
================================================== ================================
Я сталкиваюсь с той же проблемой. Вот что я решил. Поведение скрыто в 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() } }
источник
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.Это странно. Я добавляю эту строку кода в Watches in Android Monitor в режиме DEBUG и обнаружил, что она нормально возвращает 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.
источник
Применить
BottomsheetDialogFragment
состояние вonResume
решит эту проблему@Override public void onResume() { super.onResume(); if(mBehavior!=null) mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); }
onShow(DialogInterface dialog)
иpostDelayed
может вызвать сбой анимацииисточник
Все результаты с использованием 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 - это только AppCompatDialogFragment с методом onCreateDialog, который создает 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()); } .... }
источник
Самый простой способ, который я реализовал, - это как показано ниже. Здесь мы находим android.support.design.R.id.design_bottom_sheet и устанавливаем состояние нижнего листа как EXPANDED .
Без этого мой нижний лист всегда застревал в состоянии 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 } }
источник
Подобно ответу uregentx , в kotlin вы можете объявить свой класс фрагмента, который расширяется
BottomSheetDialogFragment
, и при создании представления вы можете установить состояние прослушивателя диалогового окна по умолчанию после отображения диалогового окна.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 } }
Не забудьте использовать реализацию материального дизайна в градиенте.
Также ознакомьтесь со справочником по материальному дизайну Bottom Sheets
источник
dialog?
переменная в onCreateView?dialog
является свойством классаDialogFragment
, фактически является геттером. В этом примере я использовал этот геттер для получения текущего экземпляра DialogFragment иsetOnShowListener
для него. Возможно, вы уже использовали такие инструкции в своем проекте, например, в действии, для доступа кactionBar
получателю панели действий используется, поэтому вы можете изменить этот компонент, напримерactionBar?.subtitle = "abcd"
Мой ответ более или менее совпадает с большинством приведенных выше ответов с небольшими изменениями. Вместо использования findViewById для первого поиска представления нижнего листа я предпочел не жестко кодировать идентификаторы ресурсов представления каркаса, поскольку они могут измениться в будущем.
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, и я только что обнаружил, что внутри есть метод,
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 переопределите 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 }
источник