LoginSignup
3
3

More than 5 years have passed since last update.

Andriodダイアログ FLAG_NOT_TOUCH_MODALの利用について

Last updated at Posted at 2016-07-12

(中国語版は後ろです。汉语版本在日语后面!)

実現したいこと

1、ダイアログ表示してる状態で、ダイアログ以外のエリアにボタンが存在すれば、ボタンをクリックすると、ダイアログ消す同時にボタンのクリックイベントも反応するように。
2、ダイアログ以外のエリアにListViewなどスクロールできるUIが存在すれば、スクロール同時にダイアログ消すように
3、などなど

単純に言うと、ダイアログ表示してる状態で、ダイアログ後ろのUIも操作できるように実現したいんのです。

実現

実はこの問題はそんなに難しくないかなぁと思って、android.view.Windowよくご利用の方はWindowManager.LayoutParams.FLAG_NOT_TOUCH_MODALというフラグご存知でしょう?JAVADOCを見に行くと下記のよう書いてます:

Window flag: even when this window is focusable, allow any pointer events outside of the window to be sent to the windows behind it.
Otherwise it will consume all pointer events itself, regardless of whether they are inside of the window.

では、問題を解決できるんだ!

Window window = dialog.getWindow();  
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,  
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); // Dialog以外のどころも操作できるように設定
dialog.setCanceledOnTouchOutside(true); // Dialog以外のエリアをタップするとDialog閉じるように設定

実行してみると、やっぱりダイアログ後ろのUIすべて操作できました!
でも、
ダイアログは残りのままようです!setCanceledOnTouchOutside(true)効かないんだ! :sob: :sob:

では、自分で実現してみよう!
1、ダイアログのWindowにOutsideクリックListenerを追加

window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);

2、ダイアログにOntouchを追加して、ACTION_OUTSIDEイベント発火した時に、ダイアログを消す。

Dialog dialog = new Dialog(getActivity(), R.style.modal_dialog_theme) {  
            public boolean onTouchEvent(MotionEvent event) {  
                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {  
                    dismiss();  
                }  
                return false;  
            }  
        };

以上です。

その他感想

みなさんご存知の通り、AndroidはDialogとDialogFragment二種類があって、DialogFragmentの方はLifcycleがあるのでオススメです。



汉语版本



前些天,项目要求实现一个消息弹出框,然后点击弹出框以外任何区域可以关闭该UI,这个小功能听上去很简单,一个dialog分分钟搞定。
然而设计人员又说了,点击弹出框以外任何区域关闭该UI还不够, 处于用户体验的考虑,
1. 如果弹出框以外区域存在按钮的话,点击该按钮区域时不仅要关闭对话框同时还要激活按钮事件!
2. 如果弹出框以外区域存在类似ListView等可以滚动的ViewGroup时,滑动弹出框以外区域时不仅要关闭对话框同时还要让ListView实现滚动。
3. 如果弹出框以外区域存在其他等等等等。。。。。

简单来说,当dialog显示时,dialog后面的所有View也应该处于可用状态,而不是关闭dialog之后,其他的View才能被点击或者被滚动。
其实仔细想一想并不难,熟悉android.view.Window的朋友都会知道 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 这个Flag
JavaDoc是这么解释的:

Window flag: even when this window is focusable, allow any pointer events outside of the window to be sent to the windows behind it.
Otherwise it will consume all pointer events itself, regardless of whether they are inside of the window.

OK,所有问题看似迎刃而解了!

Window window = dialog.getWindow();  
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,  
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); // 设置Dialog以外View处于可操作状态
dialog.setCanceledOnTouchOutside(true); // 点击Dialog以外区域自动关闭该dialog

运行了一下,果然,在dialog显示的状态下,所有dialog后面的View都处于可以操作的状态,只是有一个问题,在操作其他View时当前的的dialog并没有自动关闭,显然setCanceledOnTouchOutside这个方法没有起到效果!我随即就意识到,亲,我又掉进大安卓的坑里了!

既然setCanceledOnTouchOutside不好用,那么我们就自己就动手丰衣足食!填坑开始!
1. 首先给dialog的Window添加外围点击监听

window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
  1. 为Dialog添加Ontouch事件,当ACTION_OUTSIDE事件发生时,关闭该Dialog
Dialog dialog = new Dialog(getActivity(), R.style.modal_dialog_theme) {  
            public boolean onTouchEvent(MotionEvent event) {  
                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {  
                    dismiss();  
                }  
                return false;  
            }  
        };

解决!
关于Dialog使用的补充
Android中存在Dialog和DialogFragment,由于DialogFragment是存在生命周期的,所以我更加喜欢推荐大家使用后者。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3