怎样判断一个点是否落在View内,判断是否手指移动到一个View中

在3.0时代之前,要判断一个点是否落在 View 上只需要两步:第一步:得到 View 的 Rect,第二步:判断点是否再这个 Rect 内。

但从 Android 3.0 开始这样的简单日子就结束了。

原因在于,Google 为 Android 3.0 提供了一套新的动画框架:Property Animation 。View/ViewGroup为此获得了强大的动画能力,但代价是View/ViewGroup的实现比以前更复杂了。3.0 前的 View/ViewGroup 在被画到画布前只会经过一次矩阵变换(如果用户不自定义的话);但现在,View/ViewGroup 的矩阵变换变为两次,并且一些新的方法和字段被添加到 View/ViewGroup 类,这些都增加了 View/ViewGroup 类的复杂程度。

Android 3.0前的View类并不持有matrix,只有canvas才持有matrix,View只有通过修改canvas的matrix才能完成一些矩阵的基 本操作(移位、旋转、缩放)。但从3.0开始,为了更好的用户体验,Google让View也亲自持有 matrix(由于这个matrix是为 property animation 服务的,因此就称它为 property matrix),这让 View 在每次 Draw 时增加了一层新的矩阵变换。但仅仅在『绘制』时增加一层转换是不够的,因为绘制只是输出,View还要处理输入(比如touch event),因此View还需要为触摸处理加一层矩阵变换。简而言之,所有和输入、输出相关的操作都需要加一层 property matrix 矩阵变换。

具体来说, View 持有的 property matrix 在 DispatchDraw 和 DispatchTouchEvent 分别被计算一次:DispatchDraw 时会用 Bitmap 乘以这个矩阵;DispatchTouchEvent 则会用 MotionEvent 的坐标(x,y)乘以这个矩阵的逆矩阵(Matrix.invert)。这可以理解为,显示的时候 view 要处理的是自己的bitmap,而在处理 motionevent 的时候,view需要处理的是 motion event。

再回到最初的问题:怎样判断某个点落在一个View里。

View 提供一个方法 getHitRect() ,文档说它的作用是 Hit rectangle in parent‘s coordinates。但实际上这个方法得到的结果是错误的。那么 getHitRect() 有什么问题呢,先看看 getHitRect() 的实现:

/**
 * Hit rectangle in parent‘s coordinates
 *
 * @param outRect The hit rectangle of the view.
 */
public void getHitRect(Rect outRect) {
    updateMatrix();
    final TransformationInfo info = mTransformationInfo;
    if (info == null || info.mMatrixIsIdentity || mAttachInfo == null) {
        outRect.set(mLeft, mTop, mRight, mBottom);
    } else {
        final RectF tmpRect = mAttachInfo.mTmpTransformRect;
        tmpRect.set(-info.mPivotX, -info.mPivotY,
                getWidth() - info.mPivotX, getHeight() - info.mPivotY);
        info.mMatrix.mapRect(tmpRect);
        outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop,
                (int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop);
    }
}

getHitRect 的做法是:计算出view在矩阵变换前的rect,然后再对这个rect进行一次矩阵变换,得到当前在parent的rect。 因此关键在 getHitRect() 是如何得到原始 rect 的,它以:

left:   -info.mPivotX,
top:    -info.mPivotY,
right:  getWidth() - info.mPivotX,
bottom: getHeight() - info.mPivotY);

这样的数据作为原始 rect 。

这样做是错的。原因有两个:

  1. 没有引入 mScrollX 和 mScrollY
  2. -info.mPivotX 不能作为 left; -info.mPivotY 不能作为 top; getWidth() - info.mPivotX 不能作为 right; getHeight() - info.mPivotY) 也不能作为 bottom。

第二个错误简单的想象一下就能得到答案:假设在窗口坐标 (0, 0, 2, 2) 有一个矩形,然后以 [1, 1] 为中轴缩放 0.5 倍,这样就得到了一个 (0.5, 0.5, 1.5, 1.5) 的矩形,显然 getHitRect() 不能得到正确的结果。

因此,怎样判断某个点是否落在在一个 View 里就得重新写一个方法,代码如下:

/**
 * Returns true if a child view contains the specified point when transformed
 * into its coordinate space.
 */
private boolean isTransformedTouchPointInView(float x, float y, View child,
                                              PointF outLocalPoint) {
    // get x, y offset
    float localX = x + getScrollX() - child.getLeft();
    float localY = y + getScrollY() - child.getTop();

    // restore location
    final float[] localXY = new float[2];
    localXY[0] = localX;
    localXY[1] = localY;
    final Matrix inverseMatrix = new Matrix();
    child.getMatrix().invert(inverseMatrix);
    inverseMatrix.mapPoints(localXY);
    localX = localXY[0];
    localY = localXY[1];

    // fill out data
    final boolean isInView = pointInView(child, localX, localY);
    if (isInView && outLocalPoint != null) {
        outLocalPoint.set(localX, localY);
    }
    return isInView;
}

/**
* Determines whether the given point, in local coordinates is inside the view.
*/
private static boolean pointInView(View view, float localX, float localY) {
    return localX >= 0 && localX < (view.getRight() - view.getLeft())
        && localY >= 0 && localY < (view.getBottom()- view.getTop());
}

上面的方法将点(x,y)用view的inverse matrix进行了一次变换,然后再判断变换后的点是否落在view的rect内。

刚接触到 Property Animation 时我觉得它真是一个非常cool的结构,至少从使用者的角度来看是。但现在我认为它并不是一个成熟的方案,因为一些复杂的情况[注1] google 并没有做考虑,所以,在使用 Property Animation 和与 Property Animation 有关的 api 时一定要小心。



注1: 另一个复杂的情况是对View用多点触摸进行scale操作。由于 MotionEvent 没有 getRawX(int) 方法,它就只能用 getX(int) 来模拟 getRawX(int) ,getX(int)使用的是相对于ViewGroup的坐标,这会导致scale计算出错。

时间: 2024-08-15 20:37:42

怎样判断一个点是否落在View内,判断是否手指移动到一个View中的相关文章

android根据子view里面的数量自动排版的一个ViewGroup

很多时候 android系统给我们的控件不能满足我们的需求,要使界面做的好,所以大部分都是自己写的控件 . 在我做一个 短信群发界面时,需要 这个 自适应子view数量 改变自身高度的 view 所以就自写了一个 这样的控件 现 分享出来 和大家一起共享.  当然  如果朋友 下载了  添加了一些新功能  也请 帮忙 分享 出来 谢谢. 大家一起成长. 废话少说 上代码: MainActivity public class MainActivity extends ActionBarActivi

在10000以内判断一个整数,它加上100和加上268后都是一个完全平方数 3 提问:请问该数是多少?

1 ''' 2 在10000以内判断一个整数,它加上100和加上268后都是一个完全平方数 3 提问:请问该数是多少? 4 ''' 5 import math 6 for i in range(10000): 7 m = math.sqrt(i + 100) 8 n = math.sqrt(i + 268) 9 if m * m == i + 100 and n * n == i + 268: 10 print(i) 原文地址:https://www.cnblogs.com/JerryZao/p

Android View 事件分发机制源码详解(View篇)

前言 在Android View 事件分发机制源码详解(ViewGroup篇)一文中,主要对ViewGroup#dispatchTouchEvent的源码做了相应的解析,其中说到在ViewGroup把事件传递给子View的时候,会调用子View的dispatchTouchEvent,这时分两种情况,如果子View也是一个ViewGroup那么再执行同样的流程继续把事件分发下去,即调用ViewGroup#dispatchTouchEvent:如果子View只是单纯的一个View,那么调用的是Vie

解决在onCreate()过程中获取View的width和Height为0的4中方法

很经常当我们动态创建某些View时,需要通过获取他们的width和height来确定别的view的布局,但是在onCreate()获取view的width和height会得到0.view.getWidth()和view.getHeight()为0的根本原因是控件还没有完成绘制,你必须等待系统将绘制完View时,才能获得.这种情况当你需要使用动态布局(使用wrap_content或match_parent)就会出现.一般来讲在Activity.onCreate(...).onResume()方法中

已知一个函数rand5()能够生成1-5的随机数,请给出一个函数,该函数能够生成1-7的随机数。

这是朋友去笔试的一道题,有点考智商,当时我还很自信的说 random5+random5/2  不就可以了 他说不行,然后我就在网上搜了一下 有一道类似的题目 题目: 已知一个函数rand7()能够生成1-7的随机数,请给出一个函数,该函数能够生成1-10的随机数. 思路: 假如已知一个函数能够生成1-49的随机数,那么如何以此生成1-10的随机数呢? 解法: 该解法基于一种叫做拒绝采样的方法.主要思想是只要产生一个目标范围内的随机数,则直接返回.如果产生的随机数不在目标范围内,则丢弃该值,重新取

如何检测一个圆在多个圆内?

问题定义: 存在多个半径相同的圆,和一个半径不同的圆,如何判断半径不同的圆完全在一群圆内.下图演示了几种情况,左边是完全在圆内,右边不是. 解决方法之一: 对于红圆在某个黑圆之内或者在所有黑圆之外等的特例情形,可以用简单的圆圆之间的几何判断算法得到结果,对于其余部分相交的一般情形,如果同时满足以下两个条件则红圆在黑圆内: 1. 红圆与所有黑圆的交点都在黑圆内: 2. 黑圆之间的交点如果在红圆内,则其也必然在黑圆内. 否则,红圆不在黑圆内.

A Round Peg in a Ground Hole - POJ 1584 (判断凸多边形&amp;判断点在多边形内&amp;判断圆在多边形内)

题目大意:首先给一个圆的半径和圆心,然后给一个多边形的所有点(多边形按照顺时针或者逆时针给的),求,这个多边形是否是凸多边形,如果是凸多边形在判断这个圆是否在这个凸多边形内. 分析:判断凸多边形可以使用相邻的三个点叉积判断,因为不知道顺时针还是逆时针,所以叉积如果有有整数和负数,那么一定不是凸多边形(注意允许多多点在一条线段上).判断圆在凸多边形首先要判断圆心是否在多边形内,如果在多边形内,再次判断圆心到达到变形每条边的最短距离,如果小于半径就是不合法.ps:一道好题,通过这个题学会了不少东西.

sgu139Help Needed!判断15数码是否有解,以及判断N数码是否有解的推论

是这样的,要你判断一个15数码是否有解. 我不会,找了这样一个方法. 将16个数按出现顺序存放在一维数组里面, 然后累加每个数的逆序对数目, 还要加上0到终态的曼哈顿距离,得到一个数x. 由于最后的状态,整个图的逆序对数目是15, 是个奇数,于是,如果x也是奇数, 那么这个15数码就有解, 否则无解. 其它各种数码,都按照此法解就是了, 我的代码如下: #include<iostream> using namespace std; int main() { int map[16],ans=0;

Android View体系(八)从源代码解析View的layout和draw流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源代码解析Scroller Android View体系(五)从源代码解析View的事件分发机制 Android View体系(六)从源代码解析Activity的构成 Android View体系(七)从源代码解析View的measure流程 前言 上一篇文章我们讲了View的measure的流程.接