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 system
   final 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内的时候,这个点不是手指所点击的坐标,而是手指点击的坐标加上了mScrollX和mScrollY,然后在判断是否在该子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() { 

   @Override
   public void onClick(View v) {
    Toast.makeText(context, "点击了线性布局2", 0).show(); 

   }
  });
  btn.setOnClickListener(new OnClickListener() { 

   @Override
   public void onClick(View v) {
    Toast.makeText(context, "点击了Button", 0).show();
   }
  });
 } 

 @Override
 protected 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);
  }
 } 

 @Override
 protected 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{ 

  @Override
  public boolean onTouch(View v, MotionEvent event) { 

   detector.onTouchEvent(event);
   return true;
  } 

 } 

 private class MyOnGestureListener extends SimpleOnGestureListener{
  @Override
  public 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 { 

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(new MyViewGroup(this));
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android中捕捉menu按键点击事件的方法

    本文实例讲述了Android中捕捉menu按键点击事件的方法.分享给大家供大家参考.具体如下: @Override public boolean onCreateOptionsMenu(Menu menu) { /* * add()方法的四个参数,依次是: 1.组别,如果不分组的话就写Menu.NONE, * 2.Id,这个很重要,Android根据这个Id来确定不同的菜单 3.顺序,那个菜单现在在前面由这个参数的大小决定 * 4.文本,菜单的显示文本 */ menu.add(Menu.NONE

  • Android 中ListView的Item点击事件失效的快速解决方法

    在平常的开发过程中,我们的ListView可能不只是简单的显示下文本或者按钮,更多的是显示复杂的布局,这样的话,我们就得自己写布局和自定义adapter了,一般是继承于BaseAdapter,示例代码见下方.写ListView的点击事件时OnItemClickListener,onItemClick方法没有执行,导致ListView中Item条目点击事件失效,而Item中的View点击事件可以在getView方法中进行处理.导致整个Item点击失效的原因多半是由于在[你自己定义的Item中存在诸

  • Android中父View和子view的点击事件处理问题探讨

    android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解. 一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP 当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewG

  • 用Kotlin实现Android点击事件的方法

    近期,Google宣布Kotlin成为了Android一级开发语言.于是就刚刚简单的研究了一下,查资料的时候发现现成的资料还是很少的,于是决定自己记录一下,方便以后查看,也供其他人一个参考. 在android中,点击事件大致分为三种写法: 1. 匿名内部类. 2. Activity实现全局OnClickListener接口. 3. 指定xml的onClick属性. 今天用Kotlin实现这三种方式实现点击事件 匿名内部类:这种方式最简单 override fun onCreate(savedIn

  • Android中捕获TTextView文本中的链接点击事件方法

    Android中的TTextView很强大,我们可以不仅可以设置纯文本为其内容,还可以设置包含网址和电子邮件地址的内容,并且使得这些点击可以点击.但是我们可以捕获并控制这些链接的点击事件么,当然是可以的. 本文将一个超级简单的例子介绍一下如何实现在Android TextView 捕获链接的点击事件. 关键实现 实现原理就是将所有的URL设置成ClickSpan,然后在它的onClick事件中加入你想要的控制逻辑就可以了. 复制代码 代码如下: private void setLinkClick

  • 简单讲解Android开发中触摸和点击事件的相关编程方法

    在Android上,不止一个途径来侦听用户和应用程序之间交互的事件.对于用户界面里的事件,侦听方法就是从与用户交互的特定视图对象截获这些事件.视图类提供了相应的手段. 在各种用来组建布局的视图类里面,你可能会注意到一些公共的回调方法看起来对用户界面事件有用.这些方法在该对象的相关动作发生时被Android框架调用.比如,当一个视图(如一个按钮)被触摸时,该对象上的onTouchEvent()方法会被调用.不过,为了侦听这个事件,你必须扩展这个类并重写该方法.很明显,扩展每个你想使用的视图对象(只

  • Android捕获点击事件范围的方法

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

  • Android给TextView添加点击事件的实现方法

    首先设定TextView的clickable属性为true. 可以在布局文件中进行设定,比如: <TextView android:id="@+id/phone" android:clickable="true" --------->设定此属性 android:layout_marginLeft="10dp" android:layout_below="@id/address" android:layout_toR

  • Android中点击事件的四种写法详解

    Android中点击事件的四种写法 使用内部类实现点击事件 使用匿名内部类实现点击事件 让MainActivity实现View.OnClickListener接口 通过布局文件中控件的属性 第一种方法:使用内部类 基本步骤如下: 1. 新建一个MyOnClickListener类并实现View.OnClickListener接口 2. 重写View.OnClickListener接口中的OnClick(View view)方法 3. 给Button绑定一个监听器,并监听一个点击事件 示例代码如下

  • Android点击事件派发机制源码分析

    概述 一直想写篇关于Android事件派发机制的文章,却一直没写,这两天刚好是周末,有时间了,想想写一篇吧,不然总是只停留在会用的层次上但是无法了解其内部机制.我用的是4.4源码,打开看看,挺复杂的,尤其是事件是怎么从Activity派发出来的,太费解了.了解Windows消息机制的人会发现,觉得Android的事件派发机制和Windows的消息派发机制挺像的,其实这是一种典型的消息"冒泡"机制,很多平台采用这个机制,消息最先到达最底层View,然后它先进行判断是不是它所需要的,否则就

  • Android点击事件之多点触摸与手势识别的实现

    前言 最近遇到想要实现三指滑动监听的需求,实现代码不方便贴出来,但是思路还是可以记录一下. Muilti-touch 双指缩放探索 首先要实现OnTouchListener接口,然后重写方法: public boolean onTouch(View v, MotionEvent event); 从这个方法中我们就可以获取实现两指缩放功能的全部信息. View v是触发事件的源,MotionEvent event即一个触摸事件.对屏幕的几乎所有操作都会触发事件,如点击.放开.滑动等. 不同的事件在

  • jQuery实现按钮只点击一次后就取消点击事件绑定的方法

    本文实例讲述了jQuery实现按钮只点击一次后就取消点击事件绑定的方法.分享给大家供大家参考.具体实现方法如下: <input type="button" id="my-selector" value="只能点击一次" /> <script> $('#my-selector').bind('click', function() { $(this).unbind('click'); alert('Clicked and un

  • jquery无法为动态生成的元素添加点击事件的解决方法(推荐)

    遇到 jquery无法为动态生成的元素添加点击事件,谷歌一下,整理一下解决方法如下: (<li>中间的元素是动态生成的), 现在想为<i>添加点击事件, 例子如下: <div> <ul> <li> <span> <i class='icon'>这是元素内容</i> //i是动态生成 </span> </li> </ul> </div> 解决方法如下: $(docu

  • layui禁用侧边导航栏点击事件的解决方法

    直接上代码吧 //JavaScript代码区域 layui.use(['element', 'laypage'],function(){ var element = layui.element; element.on('nav(test)', function(elem){ $(".layui-nav-tree").find(".layui-nav-child").css("display","contents"); });

  • Android开发-之监听button点击事件的多种方法

    在Android下,事件的发生是在监听器下进行,android系统可以响应按键事件和触摸屏事件,本文主要介绍了button点击事件的方法 一.实现button点击事件的方法 实现button点击事件的监听方法有很多种,这里总结了常用的四种方法: 1.匿名内部类 2.外部类(独立类) 3.实现OnClickListener接口 4.添加XML属性 每一种方法都有它的优点也有它的不足,那么接下来就来详细的讲解这四个实现方法 二.具体实现 1.匿名内部类: 在Android开发中我们会经常看到各种匿名

随机推荐