问题: 判断parent view中的手势是否出现在旋转之后的subview的范围中的解决方法(iOS&Android)
解决思路: 取出点击位置的point, 然后translate到subview的坐标系中, 判断是否在subview的矩形中即可.
iOS:
现象:

代码:

结果:

部分解释: iOS中的view有frame/bounds/center/transform等坐标属性,
四者的区别(具体可查看文档, 非常详细)
frame
: 相对于parent view的坐标系
bounds
: 相对于自己的坐标系
center
: view的中心点(跟layer的anchorPoint有关, anchorPoint默认值是(0.5, 0.5), 所以是中心点)
transform
: 相对于center的缩放/旋转等二维操作. layer中有个transform是三维的.
三者之间的关系是-bounds/center/transform会影响frame的值.
大部分情况, 我们使用的frame
(x, y, width, height)只影响到了bounds
和center
, 此时transform
是初始值CGAffineTransformIdentity
; 而当我们要设置transform
的时候, frame的值就会变得很奇怪. 其实frame
还是那个矩形, 不过它代表的含义是能包含这个view的最小矩形而已. 因为transform
涉及到scale/rotation等操作, 所以frame
看起来和我们真正想要的值不太一样, 而这时候就是需要分开使用center/bounds/transform的时候, 不能单独设置frame.
layer
的相关属性不介绍, 有兴趣可以直接查看API文档.
Android:
类似的, 我开始在Android中寻找相应的解决方案. 首先需要明白的几个点
- Android的每一个view也有自己的坐标系, 左上角是原点(0, 0)
- Android的view的基础坐标属性是left/top/right/bottom. width和height是由前四个值推算得来.
- Android的view的matrix作用对应于iOS的view的transform.
解决思路还是上面所述, 但是取出点击的point容易, translate到subview的坐标系中遇到了一些问题, iOS中此api存在于UIView中, 而Android的View却没有. 搜索一圈, 还是在Android的事件分发的源码中获取答案.
现象:

代码:

布局代码:

结果:

部分解释:
代码部分借鉴了Android源码.

- 先deep copy出一份新的event.
- 设置event的offsetLocation.
- 给event应用subview的inverse matrix.
- 还有很多类似对应的属性(后边继续学习, 本次没有用到)
然后event就被成功的转换到旋转后的view的坐标系中(斜着的), 原点是左上角.
遇到的问题:
- ev.offsetLocation: 因为我是在activity中override onTouchEvent, 所以在计算deltaX和deltaY的时候先减去了relativeLayout距离window的x和y, 再减去了demoTextView的left和top. 1.为什么不直接减去demoTextView距离window的x和y? 因为我发现, demoTextView被旋转之后, 它的left/top/right/bottom并没有改变, 从现象的截图里我们也可以看出. 而demoTextView.getLocationInWindow获取的point却是旋转之后的值, 从log中可以看出. 2.为什么不减去relativeLayout的left和top, 而是减去距离window的x和y? 因为relativeLayout只是activity的contentView, 外层还有多少ViewGroup是未知, 所以直接减去距离window的x和y.
- ev.transform: 源码里面的写法是
transformedEvent.transform(child.getInverseMatrix());
, 而我自己去调用demoTextView.getInverseMatrix()
的时候, 一直报错, 而且API文档中并没有这个方法, 所以就直接去Matrix的类里面去搜关键词inverse
, 发现有这样的方法可以使用.