当点击对话框外部时,如何触发事件?

52
我想知道如何解决我遇到的问题。
我有一个弹出对话框,在活动中弹出。该对话框并未覆盖整个屏幕,因此活动中的按钮仍然显示。我可以通过 dialog.setCanceledOnTouchOutside(true); 轻松关闭对话框,当触摸对话框范围之外时。
然而,我想要的是,如果在对话框范围之外点击(例如,如果有人触摸主活动上的按钮),则关闭对话框并同时触发事件。

1
通常的解决方案是使对话框模态化。为什么你不想这样做呢? - Philip Sheard
谢谢。我正在尝试在我的主活动中显示一个Web视图(在对话框中),以便它保持应用程序的外观。 - fizo07
9个回答

85

dialog.setCanceledOnTouchOutside(true); 时,您只需像这样覆盖 onCancel()

dialog.setOnCancelListener(
        new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                //When you touch outside of dialog bounds, 
                //the dialog gets canceled and this method executes.
            }
        }
);

onCancel() 方法中编写您的代码,以便在对话框被取消时运行。


最佳答案。..两年后仍然参考同样的答案...不能再次投票 :) - Ranjithkumar
1
重写 Dialog.onCancel(...) 是可以的,但是 dialog.setOnCancelListener(...) 对我没有起作用。 - StepanM
1
当你说“when”是什么意思时? - dialog.setCanceledOnTouchOutside(true); 这是默认行为... - user25
这应该是被接受的答案!!多么出色的回答。 - coolcool1994

45

这个有点可行。只是我可以滚动listview但不能点击它。 - user9599745

4
DialogFragment中,您可以使用AlertDialog以及@silverTech的答案:
override fun onDismiss(dialog: DialogInterface) {
    yourMethod()
    super.onDismiss(dialog)
}

或者

override fun onCancel(dialog: DialogInterface) {
    yourMethod()
    super.onCancel(dialog)
}

1
为什么您建议在super.onDismiss()之前插入逻辑?然后为什么是在super.onCancel()之后的逻辑? - AJW
1
@AJW,我打开了DialogFragmentonCancel是空的,而onDismiss包含一些有关解除(关闭)对话框的逻辑。我建议先编写您的逻辑,然后再销毁方法(super.onPause(), onDestroy, onSaveInstanceState等)。在系统销毁对象之前最好完成变量的最终操作。因此,也许最好删除super.onCancel(dialog)。我在您的评论后重写了onCancel,谢谢。 - CoolMind

2

您可以使用OnCancelListener在对话框外发生点击时触发事件:

dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    @Override
    public void onCancel(DialogInterface dialog) {
        yourFunction();
    }
});

它与@WillNeithan的答案(https://dev59.com/cGkw5IYBdhLWcg3w_Pfn#28709752)有何不同? - CoolMind

1
如果您在自定义对话框类中,并希望捕获“点击外部”事件 - 请重写cancel()。如果您想捕获“对话框关闭”事件 - 请重写dismiss()。我建议在super.dismiss()之前插入逻辑。Kotlin示例:
override fun dismiss() {
    Utils.hideKeyboard(mContext, window)
    super.dismiss()
}

不是一个很好的答案。意思是,它不是通用的。我必须编写新的行为,而不是注意旧的行为。在我制作通用对话框工具的情况下,我无法知道对话框背后的内容。 - user9599745
@JaveneCPPMcGowan,你可以使用自定义对话框中的回调函数来操作视图。 - SilverTech
就像我说的,那是新行为。 - user9599745
@SilverTech 为什么建议在 super.dismiss() 前插入逻辑? - AJW
另一种方式创建了这样一种情况,只有有时候hideKeyboard函数才能按预期工作。 - SilverTech

1

它可在com.google.android.material.bottomsheet.BottomSheetDialog中找到。

> // We treat the CoordinatorLayout as outside the dialog though it is technically inside
>     coordinator
>         .findViewById(R.id.touch_outside)
>         .setOnClickListener(
>             new View.OnClickListener() {
>               @Override
>               public void onClick(View view) {
>                 if (cancelable && isShowing() && shouldWindowCloseOnTouchOutside()) {
>                   cancel();
>                 }
>               }
>             });

所以您可以在底部对话框中覆盖ClickListener

class MyBottomDialogFragment : BottomSheetDialogFragment(){
 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = object : BottomSheetDialog(activity as Context, theme) {
            private var isDialogCancelable: Boolean = false
            private var isDialogCancelableOnTouchOutside: Boolean = false

            override fun onBackPressed() {
                handleBackPressed(this)
            }

            override fun setContentView(layoutResId: Int) {
                super.setContentView(layoutResId)
                setupTouchOutside()
            }

            override fun setContentView(view: View?) {
                super.setContentView(view)
                setupTouchOutside()
            }

            override fun setContentView(view: View?, params: ViewGroup.LayoutParams?) {
                super.setContentView(view, params)
                setupTouchOutside()
            }

            private fun setupTouchOutside() {
                val coordinator = findViewById<View>(com.google.android.material.R.id.coordinator) as CoordinatorLayout
                // We treat the CoordinatorLayout as outside the dialog though it is technically inside
                coordinator
                    .findViewById<View>(com.google.android.material.R.id.touch_outside)
                    .setOnClickListener {
                        if (isDialogCancelable && isShowing && isDialogCancelableOnTouchOutside) {
                            handleTouchOutside(this)
                        }
                    }
            }


            override fun setCancelable(cancelable: Boolean) {
                super.setCancelable(cancelable)
                isDialogCancelable = cancelable
            }

            override fun setCanceledOnTouchOutside(cancel: Boolean) {
                super.setCanceledOnTouchOutside(cancel)
                isDialogCancelableOnTouchOutside = cancel
            }
        }
        dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
        dialog.setCanceledOnTouchOutside(true)
        return dialog
    }

  protected open fun handleBackPressed(dialog: BottomSheetDialog) {
        /* do what you want but after that call dialog.cancel() or dialog.dismiss() */
    }

    protected open fun handleTouchOutside(dialog: BottomSheetDialog) {
        /* do what you want but after that call dialog.cancel() or dialog.dismiss() */        
    }
}

0

我发现其他答案都很冗长和复杂,所以我采用了以下方法:

步骤1: 为您想要生成单击外部事件的元素的外部容器创建一个ID。

在我的情况下,这是一个线性布局,我已经将其ID设置为“outsideContainer”

步骤2: 为该外部容器设置一个onTouchListener,它将简单地作为内部元素的单击外部事件!

outsideContainer.setOnTouchListener(new View.OnTouchListener() {
                                            @Override
                                            public boolean onTouch(View v, MotionEvent event) {
                                                // perform your intended action for click outside here
                                                Toast.makeText(YourActivity.this, "Clicked outside!", Toast.LENGTH_SHORT).show();
                                                return false;
                                            }
                                        }
    );

0

使用返回按钮命令返回到上一个屏幕。

在扩展 DialogFragment 的类中使用覆盖方法 onCancel...

@Override
    public void onCancel(@NonNull DialogInterface dialog)
    {
        super.onCancel(dialog);
        getParentFragment().getActivity().onBackPressed();
    }

0

@WillNeithan的答案在Kotlin中:

val alertDialog = AlertDialog.Builder(this).create()
// ... complete setting up alertDialog
    
alertDialog.setOnCancelListener(
    DialogInterface.OnCancelListener {
        println("User Tapped Outside to Dismiss alertDialog")                 
    }
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接