Задайте состояние BottomSheetDialogFragment для расширенного
Как установить состояние фрагмента, расширяющего расширение BottomSheetDialogFragment
, с помощью BottomSheetBehavior#setState(STATE_EXPANDED)
, используя библиотеку дизайна поддержки Android (v23.2.1)?
https://code.google.com/p/android/issues/detail?id=202396 говорит:
Нижние листы сначала устанавливаются на STATE_COLLAPSED. Вызов BottomSheetBehavior # setState (STATE_EXPANDED), если вы хотите его развернуть. Обратите внимание, что вы не можете вызвать метод перед макетами представления.
Рекомендуемая практика требует предварительного просмотра, но я не уверен, как я установлю BottomSheetBehaviour на фрагмент ( BottomSheetDialogFragment
).
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
Ответы
Ответ 1
"Обратите внимание, что вы не можете вызвать метод перед просмотром макетов".
Приведенный выше текст является ключом.
У диалогов есть слушатель, который запускается, когда диалоговое окно отображается. Диалог не может быть показан, если он не выложен.
Итак, в onCreateDialog()
вашего модального нижнего листа (BottomSheetFragment
) непосредственно перед возвратом диалогового окна (или в любом месте, когда у вас есть ссылка на диалоговое окно), вызовите:
// This listener 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
.
Ответ 2
Ответ на efeturi велик, однако, если вы хотите использовать onCreateView(), чтобы создать свой BottomSheet, в отличие от goCreateDialog(), вот код, который вам нужно будет добавить под своим 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);
}
Ответ 3
Я написал подкласс BottomSheetDialogFragment
для этого:
public 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.
Ответ 4
Применить состояние BottomsheetDialogFragment в OnResume решит эту проблему
@Override
public void onResume() {
super.onResume();
if(mBehavior!=null)
mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
onShow (диалог DialogInterface) и postDelayed может вызвать сбой анимации
Ответ 5
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.
Это странно. Я добавляю эту строку кода в часы в 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.
Ответ 6
Я думаю, что выше, тем лучше. К сожалению, я не нашел эти решения, прежде чем я решил. Но напиши мое решение. довольно похоже на все.
================================================== ================================
Я сталкиваюсь с той же проблемой. Это то, что я решил. Поведение скрыто в 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()
}
}
Ответ 7
Все результаты с использованием onShow() вызывают случайную ошибку рендеринга, когда отображается мягкая клавиатура. См. Снимок экрана ниже - Диалог BottomSheet не находится внизу экрана, а помещен как клавиатура. Эта проблема возникает не всегда, но довольно часто.
![введите описание изображения здесь]()
UPDATE
Мое решение с отражением частного участника не нужно. Использование 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());
}
....
}
Ответ 8
Самый простой способ, который я реализовал, это как показано ниже. Здесь мы находим 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
}
}
Ответ 9
Как и в случае ответа 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