BottomSheetDialogFragment - listen to dismissed by user event

后端 未结 6 1913
野性不改
野性不改 2021-01-01 08:56

How can I listen to a FINAL dismissal of a BottomSheetDialogFragment? I want to save user changes on the final dismissal only...

I tried following:

相关标签:
6条回答
  • 2021-01-01 09:41

    In an AppCompatActivity you can use the following technique:

        val mgr = supportFragmentManager
        val callback = object: FragmentManager.OnBackStackChangedListener {
            var count = 0
            override fun onBackStackChanged() {
                // We come here twice, once when the sheet is opened, 
                // once when it's closed.
                if (++count >= 2) {
                    mgr.removeOnBackStackChangedListener(this)
                    doWhatNeedsToBeDoneWhenTheSheetIsClosed()
                }
            }
        }
        mgr.addOnBackStackChangedListener(callback)
    

    Be sure to do the above just before you call show on the sheet.

    0 讨论(0)
  • 2021-01-01 09:47

    While the method of @prom85 works, there is a different one. If you want to dismiss a BottomSheetDialogFragment in one case and retain in the other, it won't work. It will close the dialog in all cases.

    For instance, if you entered text inside EditText of BottomSheetDialogFragment and occasionally clicked outside, it would close the dialog without any warning. I tried https://medium.com/@anitas3791/android-bottomsheetdialog-3871a6e9d538, it works, but in another scenario. When you drag the dialog down, it will dismiss it. If you click outside, it won't show any alert message and won't dismiss the dialog.

    So, I used a nice advice of @shijo from https://stackoverflow.com/a/50734566/2914140.

    Add these lines to onActivityCreated method (or any other life cycle method after 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);
    }
    

    In my case in yourClickListener I check the text and show an alert or dismiss the dialog:

    private fun checkAndDismiss() {
        if (newText == oldText) {
            dismissAllowingStateLoss()
        } else {
            showDismissAlert()
        }
    }
    

    When you create BottomSheetDialogFragment, don't call setCancelable(false) as in https://stackoverflow.com/a/42679131/2914140 or these methods probably won't work. And maybe don't set <item name="android:windowCloseOnTouchOutside">false</item> in styles or setCanceledOnTouchOutside(false) in onCreateDialog.


    I also tried several ways to override cancel behavior, but they didn't succeed.

    <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)
    }
    
    0 讨论(0)
  • 2021-01-01 09:48

    Although all similar questions on SO suggest using onDismiss I think following is the correct solution:

    @Override
    public void onCancel(DialogInterface dialog)
    {
        super.onCancel(dialog);
        Toast.makeText(MainApp.get(), "CANCEL", Toast.LENGTH_SHORT).show();
    }
    

    This fires if:

    * the user presses back
    * the user presses outside of the dialog
    

    This fires NOT:

    * on screen rotation and activity recreation
    

    Solution

    Combine onCancel and BottomSheetBehavior.BottomSheetCallback.onStateChanged like following:

    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();
        }
    }
    
    0 讨论(0)
  • 2021-01-01 09:56
    bottomSheetDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
       @Override
       public void onDismiss(DialogInterface dialog) {
           toast("dismissed");
       }
    });
    
    0 讨论(0)
  • 2021-01-01 09:58

    i achieved this using this simple trick

    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)
    
    0 讨论(0)
  • 2021-01-01 09:58

    If you extended from BottomSheetDialogFragment() just override in your class

     override fun onDismiss(dialog: DialogInterface) {
            super.onDismiss(dialog)
            //Code here
        }
    

    This will trigger when onBackPress and when you dismiss the dialog by clicking outside of it.

    Make sure to NOT set your dialog as cancelable because this will not fire

    0 讨论(0)
提交回复
热议问题