zoukankan      html  css  js  c++  java
  • 为什么属性动画可以响应触摸事件

    关于Android 的动画及原理,可以参考:

    逐帧动画(AnimationDrawable)

    View动画,插值器(Animation)

    属性动画浅谈(Animator)

    Android中的动画和原理(帧动画和补间动画)

    属性动画源码分析

    补间动画和属性动画内存泄露分析

    属性动画与 VIew动画的关键区别之一,就是 属性动画 view 可以在动画过程中收到点击事件,而 view 动画过程中,点击事件仍在原地;基于此,很多人认为属性动画改变了 view 的属性,导致view 的位置发生了变化,所以属性动画可以收到点击事件,这种说法其实是错误的;

    为什么如此说呢,我们这里给一个例子:

    ObjectAnimator.ofFloat(v, "translationX", 0, 1000f)
    		.setDuration(1000)
    		.start();
    

    在开发者选项中,我们打开显示布局位置:

    运行如下:

    发现上面显示的 view 的位置并没有发生改变;

    那么上面动画到底做了什么呢,我们都知道,上面动画会调用 View 的 setTranslationX 方法,我们来看源码:

        public void setTranslationX(float translationX) {
            if (translationX != getTranslationX()) {
                invalidateViewProperty(true, false);
                // Sets the translation value for the display list on the X axis.
                // 在display list 上设置了 translationX
                mRenderNode.setTranslationX(translationX);
              // 这里的invalidateViewProperty会调用invalidate方法,也就是会导致 view 重绘,但是,并不会导致 view 的位置发生改变(没有 onLayout 调用)
                invalidateViewProperty(false, true);
                invalidateParentIfNeededAndWasQuickRejected();
                notifySubtreeAccessibilityStateChangedIfNeeded();
            }
        }
    

    上面可以看出,并没有改变 view 位置,而是在display list 上设置了 translationX

    关于 display list,这个就有些复杂了,暂不分析

    那么还有一个问题需要 解决,就是为什么能收到点击事件,这就需要看父布局(ViewGroup)如何判断一个触控坐标是否在子 view 范围内了,在 ViewGroup 的 dispatchTouchEvent 事件中,有如下源码 :

    if (!canViewReceivePointerEvents(child)// 判断是否可见,是否有动画(View 动画,即 Animation)正在播放
        || !isTransformedTouchPointInView(x, y, child, null)// 判断坐标是否在 child 内
       ) {
    		continue;
    }
    
    protected boolean isTransformedTouchPointInView(float x, float y, View child,
                                                    PointF outLocalPoint) {
      final float[] point = getTempPoint();
      point[0] = x;
      point[1] = y;
      transformPointToViewLocal(point, child);// 将触摸事件的坐标点按 child 中的相反矩阵转换
      final boolean isInView = child.pointInView(point[0], point[1]);// 判断转换后的点是否在 view 的位置内
      if (isInView && outLocalPoint != null) {
        outLocalPoint.set(point[0], point[1]);
      }
      return isInView;
    }
    
    public void transformPointToViewLocal(float[] point, View child) {
      point[0] += mScrollX - child.mLeft;
      point[1] += mScrollY - child.mTop;
    
      if (!child.hasIdentityMatrix()) {// 如果设置了 translationX,这里肯定为 true
        child.getInverseMatrix().mapPoints(point);// 将触控坐标点做相反矩阵计算,这里是核心
      }
    }
    
    final boolean hasIdentityMatrix() {
      return mRenderNode.hasIdentityMatrix();
    }
    

    上面可以看出,这里在判断子view 是否可以收到触控事件时,做了触控矩阵转换,如果转换后的触控位置在子view 范围内,则认为可以收到触控事件;

    上面涉及到矩阵 Matrix 类,很多人可能不是很清楚这个,关于这些,可能需要读者自行学习,打开 Matrix 源码,我们可以看到这样一些方法:

    public void setRotate(float degrees)
    public void setScale(float sx, float sy)
    public void setTranslate(float dx, float dy)
    

    其实,在安卓中,关于 View 方面,很多平移,旋转,放大缩小,都是通过Matrix 做坐标变换来完成的,包括 Canvas,Paint等绘图基础.

  • 相关阅读:
    [PHP] Layui + jquery 实现 实用的文章自定义标签
    个人总结第五周
    个人总结第四周
    个人总结第三周
    个人总结第二周
    个人总结第一周
    用户体验评价
    第二阶段scrum冲刺
    单词统计
    用户模块和用户场景
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/12316961.html
Copyright © 2011-2022 走看看