android 触摸监听重写_Android-Dialog监听触摸外部事件_侯广的博客-程序员信息网

技术标签: android 触摸监听重写  

前言

说到Dialog,这是一个非常常见的控件,用来显示我们想显示的各种特效,各种图片文案。我们可以会有很多类似这样的需求,想要用户点击弹框外部区域不消失,想要用户点击弹框外部区域消失等。想要这样的效果只需要调用一个方法:

setCanceledOnTouchOutside(boolean cancel)

然而,有时候我们有更多的需求,比如,我们希望在用户点击弹框外部区域的时候,做一些其他的操作,这时候我们就需要监听到触摸弹框外部的事件拉,单纯使用Dialog的现有方法,貌似没法实现。

思路

想了一下,以我目前的水平,想出了两个方案:

1)使用单独的一个Activity来代替Dialog,即设置Activity背景为透明,中间布置一个View作为弹框,这样,我们通过setOnClickListener()方法,就能轻松实现监听效果了。

2)继承Dialog类,重写onTouchEvent()方法

对比了一下,方法1肯定是最容易的方法,这时候打算实现下方法2。如何实现,先从Dialog类的onTouchEvent()方法下手,因为Dialog相关的触摸事件处理逻辑肯定在这里。

Dialog的onTouchEvent()方法:

public boolean onTouchEvent(MotionEvent event) {

if (mCancelable && mShowing && mWindow.shouldCloseOnTouch(mContext, event)) {

cancel();

return true;

}

return false;

}

我们很快就看到了,在onTouchEvent()方法体里,满足了一定条件后,就会调用cancel()方法取消弹框,这里就是用户触摸弹框外部区域的判定条件

继续看到关键方法mWindow.shouldCloseOnTouch(mContext, event)

/**@hide */

public boolean shouldCloseOnTouch(Context context, MotionEvent event) {

if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN

&& isOutOfBounds(context, event) && peekDecorView() != null) {

return true;

}

return false;

}

可以看到,这里判定Dialog是否满足应该被关闭的条件是在方法isOutOfBounds(context, event),看方法名我们也可以猜出,这个方法用来判断当前用户触摸是否超出了Dialog的显示区域

isOutOfBounds(context, event)代码:

private boolean isOutOfBounds(Context context, MotionEvent event) {

final int x = (int) event.getX();

final int y = (int) event.getY();

final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();

final View decorView = getDecorView();

return (x < -slop) || (y < -slop)

|| (x > (decorView.getWidth()+slop))

|| (y > (decorView.getHeight()+slop));

}

实现

这样思路就出来了,只要我们在重写的onTouchEvent()方法里通过以上方法判断触摸是否超出弹框区域:

先实现一个基类:

/** *@author linzewu *@date 16-8-12 */

public abstract class BaseDialog extends Dialog {

public BaseDialog(Context context) {

super(context, R.style.dialog);

}

public BaseDialog(Context context, int themeResId) {

super(context, R.style.dialog);

}

protected BaseDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {

super(context, R.style.dialog);

}

protected abstract void onTouchOutside();

@Override

public boolean onTouchEvent(MotionEvent event) {

/* 触摸外部弹窗 */

if (isOutOfBounds(getContext(), event)) {

onTouchOutside();

}

return super.onTouchEvent(event);

}

private boolean isOutOfBounds(Context context, MotionEvent event) {

final int x = (int) event.getX();

final int y = (int) event.getY();

final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();

final View decorView = getWindow().getDecorView();

return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop))

|| (y > (decorView.getHeight() + slop));

}

}

继承该类

/** *@author linzewu *@date 16/8/19 */

public class SpecialDialog extends BaseGuideDialog {

public SpecialDialog(Context context) {

super(context);

}

public SpecialDialog(Context context, int themeResId) {

super(context, themeResId);

}

protected SpecialDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {

super(context, cancelable, cancelListener);

}

@Override

protected void onTouchOutside(MotionEvent event) {

//触摸了外部区域

Log.d("log", "touch outside");

}

}

补充

既然谈到Dialog,那一定经常用到cancel()和dismiss()方法,看起来这两个方法都可以用,那有什么区别呢?

还是可以从源码找答案。

dimiss()源码:

@Override

public void dismiss() {

if (Looper.myLooper() == mHandler.getLooper()) {

dismissDialog();

} else {

mHandler.post(mDismissAction);

}

}

dismiss()方法继续调用dismissDialog()方法:

void dismissDialog() {

if (mDecor == null || !mShowing) {

return;

}

if (mWindow.isDestroyed()) {

Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");

return;

}

try {

mWindowManager.removeViewImmediate(mDecor);

} finally {

if (mActionMode != null) {

mActionMode.finish();

}

mDecor = null;

mWindow.closeAllPanels();

onStop();

mShowing = false;

sendDismissMessage();

}

}

可以看到最后是调用mWindowManager.removeViewImmediate(mDecor),移除了弹框。

再来看cancel()方法源码:

public void cancel() {

if (!mCanceled && mCancelMessage != null) {

mCanceled = true;

// Obtain a new message so this dialog can be re-used

Message.obtain(mCancelMessage).sendToTarget();

}

dismiss();

}

可以看出,cancel()方法先发出cancel的消息,然后还是会调用dismiss()方法,最后也是移除Dialog.

总结:cancel()和dismiss()方法最终效果都是移除了弹框视图,但是cancel()方法体中包含调用dismiss()。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42524842/article/details/111983548

智能推荐

2021-07-07mathtype宏相关问题解决方案_单线程生物在哪儿的博客-程序员信息网_mathtype禁用宏

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

通过post向指定URL地址访问爬取数据_未来的农场主的博客-程序员信息网_通过post访问url

一:直接上代码,控制层//这是下面要用的HOST,里面的数据是F12在页面看到的,例如:127.0.0.1:8088private final static String HOST = PropertiesUtil.getDocking("url:端口号");@RequestMapping("findPackageDetails") public void findPackageDetails() { // 根据固定地址获取cookieString param ="U.

[享学Feign] 九、Feign + OkHttp和Feign + Apache HttpClient哪个更香?_方向盘(YourBatman)的博客-程序员信息网

前八篇文章介绍完了feign-core核心内容,从本篇开始将介绍它的“其它模块”。其实核心模块可以独立的work,但是不免它的能力偏弱,比如只能编码字符串类型、只能解码字符串类型,默认使用`java.net.HttpURLConnection`作为HC...本篇将介绍它的第一个模块:Client相关模块。我们知道,流行的开源Http库的性能均远高于JDK源生的`HttpURLConnection`,因此实际生产中**肯定是**用的三方库来发送Http请求。Feign它提供了`feign.Client

JS 作用域和作用域链_destinytaoer的博客-程序员信息网_js作用域和作用域链

1. 作用域作用域就是代码的执行环境,全局执行环境就是全局作用域,函数的执行环境就是私有作用域,它们都是栈内存。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。全局执行环境是最外围的一个执行环境。根据 ECMAS...

构建安全的dDoS(拒绝分布式DoS攻击) dns服务_iteye_10303的博客-程序员信息网

近日发现DNS bind9.9.1 存在严重的安全漏洞 .tail -f dns.log 发现大量不同网段ip 请求whbl.com 域名 .打开该网站看了看是个国外新闻网站. 同过日志分析发现攻击者使用了DoS攻击,采用了大量僵死云向我们服务器发起dns请求,请求频率超过2次/秒. 网上查了,有的出来个补丁但是都是针对9.3.2以下版本的.   这些请求占用了带宽.在9.9...

linux系统改装win10教程,技术编辑为你解答win10系统安装Linux Mint的图文方案_苏慕凉的博客-程序员信息网

win10系统稳定性好,使用者众多;免不了会遇到win10系统安装Linux Mint这样的问题要处理,大部分伙计都是第一次看到win10系统安装Linux Mint这样的事情,有的朋友想试着解决一下win10系统安装Linux Mint的问题却不知怎样下手,其实解决方法很简单,只需采取以下两步措施:1、你需要安装新的操作系统之前做明显的事情。第一个也是最重要的一点是要备份你坐在你的计算机上的任何...

随便推点

nRF51822_nicole088的博客-程序员信息网_nrf51822

System OFF modesystem off mode是最深省电模式。 在此模式下,系统的核心功能已关闭,所有正在进行的任务都将终止。 唯一有效的机制在这种模式下响应是复位和唤醒机制。在系统关闭模式下保留一个或多个RAM块可以通过以下方式进行唤醒:1、GPIO信号2、LPCOMP模块3、复位中唤醒在进入系统关闭模式之前,用户必须确保所有正在进行的EasyDMA交易已完成。 这通...

SpringBoot 使用JDBC_風栖祈鸢的博客-程序员信息网_springboot使用jdbc

SpringBoot 使用JDBC我超,又要开始了。太久不写,全忘完了,就当 Remake 了!1. 使用JDBCSringBoot 对数据层进行访问,无论是 SQL(关系型数据库)还是 NoSQL(非关系型数据库),其底层都是采用 Spring Data 的方式进行处理的。按照之前的笔记,创建 SpringBoot-05-Data 项目,然后添加数据相关的依赖,先只添加基础的 JDBC API 和 MySQL Driver,学习一下。 进入项目,查看 pom.xml 文件,可以看到

micropython 中断_[Micropython]TPYBoardV10X教程6 按键开关,回调函数和中断_weixin_39887546的博客-程序员信息网

原创版权归山东萝卜科技有限公司所有,转载必须以链接形式注明作者和原始出处。tpyboard 开发板上有两个小按键,分别标示为 USR 和 RTS。RTS 按键属于复位按键,如果按下的话将重新擦写重启开发板,相当于将开发板断电再重启。USR按键供用户使用,且其可以通过声明一个按键对象(Switch object)进行控制。创建开关对象的方法如下:&gt;&gt;&gt;sw=pyb.Switc...

网络组Network Teaming_weixin_30555753的博客-程序员信息网

网络组: 将多个网卡聚合在一起,从而实现冗错和提高吞吐量 网络组不同于旧版中bonding技术,提供更好的性能和扩展性 网络组由内核驱动和teamd守护进程实现.有以下不同方式:runner1.roundrobin 【mode 0】轮转策略 (balance-rr)特点:1)从头到尾顺序的在每一个slave接口上面发送数据包,轮询方式往...

51Nod 1191 消灭兔子 (贪心+优先队列)_shiyicode的博客-程序员信息网

题目链接:消灭兔子题目大意 n个兔子,每个兔子都有一个血量b[i] m种箭(每种各一支),每种箭都有伤害值d[i]和价格p[i] 每个兔子只能被射一次,伤害值大于血量则死,每种箭只能用一次 问杀死所有兔子需要的最小价格为多少,若不能杀死,则No Solution m,n小于50000)思路 典型的贪心,每个兔子只能射一次,所以只能用伤害值大于其血量的箭,在此前提下,

2018秋寒假作业3—秋季学期学习总结_aobi5342的博客-程序员信息网

希望通过下学期的学习能够增强自己的自主编程能力。转载于:https://www.cnblogs.com/jk-liulei/p/10396854.html