更多问答 >>
-
每日一问 ANR的产生的原理是什么,AMS中涉及ANR的代码有哪些?
2019-06-30 22:33 -
每日一问 对于SharedPreferences你觉得有什么优缺点?
2019-07-02 23:35 -
每日一问 | getWidth, getMeasuredWidth 有什么区别?
2019-07-05 00:48 -
每日一问 对于代码中有大量的 if/else 你有什么优化思路?
2019-07-07 23:00 -
每日一问 在Activity 的 onResume 方法中 handler.postRunnable 能获取到 View 宽高吗?
2019-07-09 21:03 -
每日一问 有没有使用过 DataBinding ,有什么优点、缺点,遇到过哪些坑?
2019-06-26 00:23 -
每日一问 View中的getContext一定返回的是Activity对象吗?
2019-06-23 22:18 -
每日一问 详细的描述下自定义 View 测量时 MesureSpec.UNSPECIFIED
2019-06-20 00:26 -
每日一问 自定义 ViewGroup 的时候,关于 LayoutParams 有哪些注意事项?
2019-06-15 21:09 -
每日一问 gradle Transform API 和 Annotation Processor各有什么使用场景?能否相互替换与补充。
2019-06-13 21:54

为什么属性动画移动一个控件后,目标位置仍然能响应用户事件?
也就是说,应用了属性动画之后,该View依然可以正确地接收到事件的分派。
那就要搞清楚ViewGroup它是怎么找到这个"偷跑"了的View的。我们知道,调用View的translationXX方法之后,虽然在屏幕上的位置是变了,但是它的[left,top,right,bottom]是不会变的。来捋一遍ViewGroup分派事件的大致流程:
当手指按下时,触摸事件会经过ViewGroup中的dispatchTouchEvent方法筛选符合条件(手指在边界范围内)的子View进行分派事件(如果未被onInterceptTouchEvent拦截的话)。
那么,如果某个子View刚好应用了translation属性动画,在ViewGroup筛选子View时,直接判断触摸点是否在[left,top,right,bottom]范围内,是肯定不行的。那它是怎么判断的呢?
上面说到了"把触摸点映射到该子View的逆矩阵上",那它是怎么个映射法:
比如一个View它水平平移了200,那它所对应的逆矩阵就是水平平移了-200,
如果触摸点坐标是[500,500]的话,那么映射之后,就是[300,500],也就是反方向移动同样的距离了。可以这样来理解:
如果一个View向右移动了一个拇指的距离,当手指在它的新位置上按下的时候,
(它最终还是要判断是否在原来的边界范围内的,那只能把触摸的坐标,给转回去,转回它应用变换之前的位置上),那ViewGroup在检测到它应用了变换后,会把现在的触摸点,向左(刚刚是向右)移动一个拇指的距离(抵消),再来判断是否在该View的边界范围内。那么为什么只有属性动画可以这样,补间动画就不行呢?
View在draw的时候,会检测是否设置了Animation(补间动画),
如果有的话,会获取这个动画当前的值(旋转或位移或缩放,透明度等),应用到canvas上,然后把东西draw出来。比如设置了位移动画,当前值是向右移动了100,那么效果就等于这样: Matrix matrix = new Matrix(); matrix.setTranslate(100, 0); canvas.setMatrix(matrix);它的作用只会在draw的时候有效。
虽然大家都是操作Matrix,但是Matrix的对象不一样(属性动画操作的Matrix,是View的mRenderNode所对应的Matrix),
所以在ViewGroup筛选的时候,应用属性动画的View会被正确找到,而补间动画的不行。为此,我专门测试了一下覆盖的情况,确实如此。
补充 & 回复@15023822235
差不多,属性动画所影响的Matrix,是在View的mRenderNode中的stagingProperties里面的,这里的Matrix,每个View之间都是独立的,所以可以各自保存不同的变换状态。
而补间动画,它所操作的Matrix,其实是借用了它父容器的一个叫mChildTransformation的属性(里面有Matrix),通过getChildTransformation获得。也就是说,一个ViewGroup中,无论它有几个子View都好,在这些子View播放补间动画的时候,都是共用同一个Transformation对象的(也就是共用一个Matrix),这个对象放在ViewGroup里面。有同学可能会问:共用?不可能吧,那为什么可以同时播放好几个动画,而互相不受影响呢?
是的,在补间动画更新每一帧的时候,父容器的mChildTransformation里面的Matrix,都会被reset。
每次重置Matrix而不受影响的原因:
是因为这些补间动画,都是基于当前播放进度,来计算出绝对的动画值并应用的,保存旧动画值是没有意义的。
就拿位移动画TranslateAnimation来说,比如它要向右移动500,当前的播放进度是50%,那就是已经向右移动了250,在View更新帧的时候,就会把这个向右移动了250的Matrix应用到Canvas上,当下次更新帧时,比如进度是60%,那计算出来的偏移量就是300,这时候,已经不需要上一次的旧值250了,就算Matrix在应用前被重置了,也不影响最后的效果。属性动画的工作原理很简单,其实就是在一定的时间间隔内,通过不断地对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在属性上的动画效果。这个属性可以是任意对象的属性。
属性动画有两个非常重要的类:`ValueAnimator` 类 & `ObjectAnimator` 类,二者的区别在于:
`ValueAnimator` 类是先改变值,然后 **手动赋值** 给对象的属性从而实现动画;是 **间接** 对对象属性进行操作;而 `ValueAnimator` 类本质上是一种 **改变值** 的操作机制。`ObjectAnimator` 类是先改变值,然后 **自动赋值** 给对象的属性从而实现动画;是 **直接** 对对象属性进行操作;可以理解为:`ObjectAnimator` 更加智能、自动化程度更高。而补间动画的核心本质就是在一定的持续时间内,不断改变 Matrix 变换,并且不断刷新的过程。对于属性动画移动后,为啥在目标位置还可以响应事件的问题,我也是看了小缘的回答,才去爬了一波代码,哈哈哈,又涨姿势了。属性动画最后会调用如下代码:
@Override
public boolean onTouch(View v, MotionEvent event) { ... if (event.getAction() == MotionEvent.ACTION_MOVE) { ... mView.layout(x - widthOffset, y - heightOffset - toolbarHeight, x + widthOffset, y + heightOffset - toolbarHeight); ... } return true; }会把控件的位置重新摆放
???你这个出自哪里的源码?
有个问题请教下,像这种问题能解决实际场景中的什么问题呢?
当你要实现一个动画的时候,如果你不清楚原理,你就不知道是改宽高好呢,还是用属性动画好呢,性能有什么差距,为什么自己实现的这么卡。
最近遇到啦这个问题。效果的确如“陈小缘”所说。对于这个现象,有个其他打疑问。为啥view 移动啦,但是其getLeft getBottom等等都没有变化。“getLeft”是离父布局的距离。没有变,说明 控件只是内容作了平移????????
对,平移的是内容,即待绘制的数据。拿setTranslationX为例子,这个setTranslationX的操作是post-layout,即在layout之后的显示的操作,不影响布局layout的。 ...查看更多
对,平移的是内容,即待绘制的数据。拿setTranslationX为例子,这个setTranslationX的操作是post-layout,即在layout之后的显示的操作,不影响布局layout的。而getLeft返回的是mLeft,这个变量你可以看到他是layout变量,你可以看到在View#layout的函数中调用setFrame时候赋值的。从绘制流程来讲,setTranslationX是layout之后的,所以是不影响这个mLeft变量的。
小缘牛逼
大概意思就是
属性动画做出的变换是存在view上面的,可以在触摸事件的时候逆向检查
补间动画做出的变化是直接在画布上的,变换了就变化了,没有存值,导致group并不知道儿子的新位置?所以无法反向查找view判断事件分发区域?