BottomSheetDialogFragment - слушать упущенное пользовательским событием
Как я могу прослушать окончательное отключение BottomSheetDialogFragment
? Я хочу сохранить изменения пользователя только при окончательном увольнении...
Я пробовал следующее:
Метод 1
Это только срабатывает, если диалог отклоняется, откручивая его (не нажимайте или не нажимайте на него снаружи)
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog d = super.onCreateDialog(savedInstanceState);
d.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) dialog.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior behaviour = BottomSheetBehavior.from(bottomSheet);
behaviour.setState(BottomSheetBehavior.STATE_EXPANDED);
behaviour.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN)
{
// Bottom Sheet was dismissed by user! But this is only fired, if dialog is swiped down! Not if touch outside dismissed the dialog or the back button
Toast.makeText(MainApp.get(), "HIDDEN", Toast.LENGTH_SHORT).show();
dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
});
return d;
}
Метод 2
Это не позволяет мне отличать окончательное увольнение и то, что происходит от поворота экрана или активного отдыха...
@Override
public void onDismiss(DialogInterface dialog)
{
super.onDismiss(dialog);
// this works fine but fires one time too often for my use case, it fires on screen rotation as well, although this is a temporarily dismiss only
Toast.makeText(MainApp.get(), "DISMISSED", Toast.LENGTH_SHORT).show();
}
Вопрос
Как я могу прослушать событие, которое указывает, что пользователь завершил диалог?
Ответы
Ответ 1
Хотя все подобные вопросы на SO предлагают использовать onDismiss
, я думаю, что следующее правильное решение:
@Override
public void onCancel(DialogInterface dialog)
{
super.onCancel(dialog);
Toast.makeText(MainApp.get(), "CANCEL", Toast.LENGTH_SHORT).show();
}
Это срабатывает, если:
* the user presses back
* the user presses outside of the dialog
Это не работает:
* on screen rotation and activity recreation
Решение
Объединить вCancel и BottomSheetBehavior.BottomSheetCallback.onStateChanged, как показано ниже:
public class Dailog extends BottomSheetDialogFragment
{
@Override
public void onCancel(DialogInterface dialog)
{
super.onCancel(dialog);
handleUserExit();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog d = super.onCreateDialog(savedInstanceState);
d.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 behaviour = BottomSheetBehavior.from(bottomSheet);
behaviour.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN)
{
handleUserExit();
dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
});
return d;
}
private void handleUserExit()
{
Toast.makeText(MainApp.get(), "TODO - SAVE data or similar", Toast.LENGTH_SHORT).show();
}
}
Ответ 2
я добился этого с помощью этого простого трюка
val bottomSheetDialog = FeedbackFormsFragment.createInstance()
bottomSheetDialog.show((activity as FragmentActivity).supportFragmentManager, BOTTOM_SHEET)
// add some delay to allow the bottom sheet to be visible first so that the dialog is not null
Handler().postDelayed({
bottomSheetDialog.dialog?.setOnDismissListener {
// add code here
}
}, 1000)
Ответ 3
Хотя метод @prom85 работает, есть и другой. Если вы хотите отклонить BottomSheetDialogFragment
в одном случае и сохранить в другом, он не будет работать. Это закроет диалог во всех случаях.
Например, если вы ввели текст внутри EditText
из BottomSheetDialogFragment
и изредка щелкали снаружи, это закрывало бы диалоговое окно без какого-либо предупреждения. Я пробовал https://medium.com/@anitas3791/android-bottomsheetdialog-3871a6e9d538, работает, но по другому сценарию. Когда вы перетащите диалоговое окно вниз, оно будет отклонено. Если вы нажмете снаружи, он не будет показывать никаких предупреждений и не будет закрывать диалоговое окно.
Итак, я воспользовался хорошим советом @shijo с fooobar.com/info/1669345/....
Добавьте эти строки в метод onActivityCreated
(или любой другой метод жизненного цикла после onCreateView
).
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
View touchOutsideView = getDialog().getWindow()
.getDecorView()
.findViewById(android.support.design.R.id.touch_outside);
touchOutsideView.setOnClickListener(yourClickListener);
}
В моем случае в yourClickListener
я проверяю текст и показываю предупреждение или закрываю диалоговое окно:
private fun checkAndDismiss() {
if (newText == oldText) {
dismissAllowingStateLoss()
} else {
showDismissAlert()
}
}
Когда вы создаете BottomSheetDialogFragment
, не вызывайте setCancelable(false)
как в fooobar.com/info/1669345/... иначе эти методы, вероятно, не будут работать. И, возможно, не устанавливайте <item name="android:windowCloseOnTouchOutside">false</item>
в стилях или setCanceledOnTouchOutside(false)
в onCreateDialog
.
Я также попробовал несколько способов переопределить поведение отмены, но они не увенчались успехом.
<style name="BottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.BottomSheetDialogTheme)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener {
val bottomSheet = dialog.findViewById<View>(
android.support.design.R.id.design_bottom_sheet) as? FrameLayout
val behavior = BottomSheetBehavior.from(bottomSheet)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
//showing the different states.
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> dismiss() //if you want the modal to be dismissed when user drags the bottomsheet down
BottomSheetBehavior.STATE_EXPANDED -> {
}
BottomSheetBehavior.STATE_COLLAPSED -> {
}
BottomSheetBehavior.STATE_DRAGGING -> {
}
BottomSheetBehavior.STATE_SETTLING -> {
}
}
}
})
dialog.setOnCancelListener {
// Doesn't matter what you write here, the dialog will be closed.
}
dialog.setOnDismissListener {
// Doesn't matter what you write here, the dialog will be closed.
}
}
return dialog
}
override fun onCancel(dialog: DialogInterface?) {
// Doesn't matter what you write here, the dialog will be closed.
super.onCancel(dialog)
}
override fun onDismiss(dialog: DialogInterface?) {
// Doesn't matter what you write here, the dialog will be closed.
super.onDismiss(dialog)
}