Android控件捕获点击事件的范围

View的Tween动画过程中点击事件的位置并不会因为动画位置的改变而改变,是因为在动画过程中layout的位置实际上没有变,因此曾经一度认为View的点击事件(其实不仅仅是点击事件,包括所有的触摸事件)触发的范围是该View在layout的时候指定的left,top,right,bottom。今天才发现不完全是这样的。一切都是因为平时看代码没有仔细一点所造成了对问题理解不全面。

在这里记录一下发现问题到处理问题的过程。


自定义这样一个ViewGroup,layout两个线性布局,左边的LinearLayout覆盖全屏幕,右面的LinearLayout在屏幕外面隐藏。然后观察在想做滑动的过程中,第二个LinearLayout显示出来的过程中,按钮Button和第二个线性布局的位置信息:


可以看到,在向左滑第二个线性布显示出来的过程中,他的位置并没有变,这里指的是通过getLeft()getTop()getRight()getBottom()获得的位置,也就是由layout决定的位置。

既然位置并没有改变,那么这时候点击第二个线性布局和按钮点击事件也被响应了,就说明捕获点击事件的位置并不完全是在layout的位置。因为并没有将手伸到屏幕外面去点击…


回头来看ViewGroup#dispatchTouchEvent方法在分发触摸事件的时候:

for (int i = count - 1; i >= 0; i--) {final View child = children[i];if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE|| child.getAnimation() != null) {child.getHitRect(frame);if (frame.contains(scrolledXInt, scrolledYInt)) {// offset the event to the view's coordinate systemfinal float xc = scrolledXFloat - child.mLeft;final float yc = scrolledYFloat - child.mTop;ev.setLocation(xc, yc);child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;if (child.dispatchTouchEvent(ev))  {// Event handled, we have a target now.mMotionTarget = child;return true;}}
} 

其中frame.contains(scrolledXInt, scrolledYInt)函数就是判断点(scrolledXInt,scrolledYInt)是不是在frame矩形里面。这个矩形frame是由child.getHitRect(frame);获得的:

    public void getHitRect(Rect outRect) {outRect.set(mLeft, mTop, mRight, mBottom);
}

显然这个矩形就是由该子View的Layout的布局参数所决定的。但是scrolledXInt和scrolledYInt参数,并不是我们手指点击的位置:

final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
……
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;

可以看出,在判断这个点是否包含在子View内的时候,这个点不是手指所点击的坐标,而是手指点击的坐标加上了mScrollXmScrollY,然后在判断是否在该子View的范围里面。

现在思考向左滑动的过程中,虽然第二个线性布局的位置没有变,还是layout的参数位置,是:mLeft:720,mTop:0,mRight:1440,mBottom:1134。

但是他的父View的mScrollX改变了,向左滑mScrollX大于0,这是用手点击第二个线性布局,手所点击的位置再加上mScrollX的值,这时就会落在了第二个线性布局的layout的范围里面。

 

测试代码:

自定义MyViewGroup:

public class MyViewGroup extends ViewGroup {public static final String TAG = "MyViewGroup";private int childCount;private GestureDetector detector;private Button btn;private LinearLayout ll2;public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}public MyViewGroup(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public MyViewGroup(Context context) {super(context);init(context);}private void init(final Context context) {detector = new GestureDetector(context, new MyOnGestureListener());LinearLayout ll1 = new LinearLayout(context);ll1.setBackgroundColor(Color.BLUE);ll2 = new LinearLayout(context);ll2.setBackgroundColor(Color.RED);btn = new Button(context);btn.setText("点击按钮");ll2.addView(btn);addView(ll1);addView(ll2);setOnTouchListener(new MyTouchEvent());ll2.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "点击了线性布局2", 0).show();}});btn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "点击了Button", 0).show();}});}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);childCount = getChildCount();for (int i = 0; i < childCount; i++) {View child = getChildAt(i);child.measure(widthMeasureSpec,heightMeasureSpec);}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {for (int i = 0; i < childCount; i++) {View child = getChildAt(i);child.layout(0+i*getWidth(), 0, (i+1)*getWidth(), getHeight());}}private class MyTouchEvent implements View.OnTouchListener{@Overridepublic boolean onTouch(View v, MotionEvent event) {detector.onTouchEvent(event);return true;}}private class MyOnGestureListener extends SimpleOnGestureListener{@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {scrollBy((int) distanceX, 0);if (getScrollX()% 10 == 0) {				Log.i(TAG, "Button左上右下位置:" + btn.getLeft() + "/"+ btn.getTop() + "/"+ btn.getRight() + "/"+ btn.getBottom());Log.i(TAG, "线性布局2的左上右下位置:" + ll2.getLeft() + "/"+ ll2.getTop() + "/"+ ll2.getRight() + "/"+ ll2.getBottom());Log.i(TAG, "MyViewGroup的mScrollX:" + getScrollX());}return super.onScroll(e1, e2, distanceX, distanceY);}}
}

然后在Activity里面:

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new MyViewGroup(this));}
}






转载于:https://www.cnblogs.com/qhyuan1992/p/5385337.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/406684.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用闭包实现onclick事件传递参数

当触发点击事件&#xff0c;一个简单的例子。 <!DOCTYPE html> <html> <head><title>闭包创建数组</title><meta charset"utf-8"><script type"text/javascript">window.onload function (){var lis documen…

Python中文全攻略

From: http://www.sqlite.com.cn/MySqlite/11/395.Html 1. 在Python中使用中文 在Python中有两种默认的字符串&#xff1a;str和unicode。在Python中一定要注意区分“Unicode字符串”和“unicode对象”的区别。后面所有的“unicode字符串”指的都是python里的“unicode…

HTTP 304状态码的详细讲解

HTTP 304状态码的详细讲解 304状态码或许不应该认为是一种错误&#xff0c;而是对客户端有缓存情况下服务端的一种响应。 整个请求响应过程如下&#xff1a; 客户端在请求一个文件的时候&#xff0c;发现自己缓存的文件有 Last Modified &#xff0c;那么在请求中会包含 If …

[MySQL FAQ]系列 -- 数据不算大,备份却非常慢

作/译者&#xff1a;叶金荣&#xff08;Email: &#xff09;&#xff0c;来源&#xff1a;http://imysql.cn&#xff0c;转载请注明作/译者和出处&#xff0c;并且不能用于商业用途&#xff0c;违者必究。问题环境硬件&#xff1a;DELL 1950, 146G SAS 15K RPMS * 2, 8G Ram软件…

视频编解码:第一章 编解码基础

1. 视频编码概念 视频编码方式就是指通过特定的压缩技术&#xff0c;将某个视频格式的文件转换成另一种视频格式文件的方式。 2. 为什么要进行视频压缩&#xff1f; 数据太大&#xff1a;未经压缩的数字视频数据量巨大存储困难&#xff1a;一张DVD只能存储几秒钟的未压缩数字视…

Python 文件读和写

转载于:https://www.cnblogs.com/nzyjlr/p/4157582.html

element-ui 设置菜单栏展开

element-ui 侧边栏默认要全部展开怎么做&#xff1f; element-ui文档中是这么写的 default-openeds 当前打开的sub-menu的key数组 给标签加上这个属性 <el-menu class"el-menu-vertical" open"handleOpen" close"handleClose" theme"…

简单JS实现走马灯效果的文字(无需jQuery)

效果类似&#xff1a;(抱歉&#xff0c;图片是静态的) 写一段html&#xff0c;需要走马灯上下跳动的内容&#xff0c;但每次只显示一行&#xff1a;<hr size"0" align"center" style"border-top: 1px solid #F5F5F5;"/> <div id"m…

Android Service 生命周期

Android Service的生命周期 Managing the Lifecycle of a Service service的生命周期&#xff0c;从它被创建开始&#xff0c;到它被销毁为止&#xff0c;可以有两条不同的路径&#xff1a; A started service 被开启的service通过其他组件调用 startService()被创建。 这种ser…

新书出版:《Android深度探索(卷1):HAL与驱动开发》

《Android深度探索&#xff08;卷1&#xff09;&#xff1a;HAL与驱动开发》【1】亚马逊【2】当当网【3】京东商城【4】互动网【5】淘宝网【6】豆瓣网 《 Android深度探索&#xff08;卷1&#xff09;&#xff1a;HAL与驱动开发》分为4篇&#xff0c;分别从搭建开发环境&#x…

async-validator 表单验证两种数据的解决方案

上问题&#xff1a; 下面选项中后台给的value 有 number和 string 两种导致无法验证 解决方案 rules: [,{type: "string",required: true,message: 编码项目至少一项,trigger: change,transform(value) {var baseif(value){//在select中转换要加这个判断//因为sele…

米的建站日记(2014年12月15日)

今天试着定义了一个音乐实体如下图&#xff1a; 然后搜了下mongdb数据库层的框架&#xff0c;发现大部分都是用spring的&#xff0c;然后又去找相关的教程和demo&#xff0c;找到了 http://blog.csdn.net/cuiran/article/details/8287204 这篇博文&#xff0c;但是用在我自己的…

RIP实验总结之一被动接口和单播更新

后续的小实验&#xff1a;RIP实验总结之二RipV2的手动汇总RIP实验总结之一rip v2的认证前言&#xff1a;以前觉得rip只需要学NA的那种水平就行了&#xff0c;但在后来自己的许多实验中不断做过不少rip相关的实验&#xff0c;在这总结一下。实验目的&#xff1a;1) 掌握r…