android View动画---操持理念

计算机软件开发 2024-9-7 20:07:53 34 0 来自 中国
本章内容: 相识View动画的总体操持理念,关键是头脑, 而非代码细节.
一. 怎样让View动起来.

1. 起主要相识View是怎样展示到屏幕上的?
①. 先确定View的位置, 如下图:
②. 在View上面绘制内容, 如下图:

2.png 2. 得出两种让View运动的方案:
①. layout() 改变结构位置
②. draw() 改变 绘制内容的位置
二. 体系接纳的时哪种方案呢?

答:第2种,  draw() 绘制时,改变绘制内容的位置.
如许做的长处:无论怎样运动, 生存了原始位置 (相对父控件的位置), 如下图:

3.png
三. View中,setScrollX、setScrollY怎样实现滚动的?

从操持者的角度看: 为了低沉耦合度, 我们应该把必要滚动的信息单独记录下来,然后在draw()绘制的时间 ,加上必要滚动的坐标, 终极的出新的绘制坐标, 如下图:


从源码实现的角度看:
以setScrollY为例:

  • 给mScrollY  变量赋值
    protected int mScrollY;    public void setScrollY(int value) {        scrollTo(mScrollX, value);    }    public void scrollTo(int x, int y) {        if (mScrollX != x || mScrollY != y) {            mScrollX = x;            mScrollY = y;            if (!awakenScrollBars()) {                postInvalidateOnAnimation();            }        }    }

  • draw()绘制, 关键代码(伪代码)
    public void draw(Canvas canvas) {        int left = mScrollX + paddingLeft;        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;        int top = mScrollY + getFadeTop(offsetRequired);        int bottom = top + getFadeHeight(offsetRequired);        canvas.saveUnclippedLayer(left、top、right、位置信息);        canvas.drawRect(left、top、right、bootm 位置信息)}四. 补间动画 和属性动画 怎样实现的?

从操持者的角度看: 原理和上面雷同, 先把动画信息先用单独对象生存起来,然后在draw()绘制的时间,举行矩阵厘革, 如下图:


从源码的角度看:
①. 补间动画生存,实在就是生存到一个变量中 mCurrentAnimation
View.java    protected Animation mCurrentAnimation = null;    public void startAnimation(Animation animation) {        ...        setAnimation(animation);         invalidate(true);    }    public void setAnimation(Animation animation) {        mCurrentAnimation = animation;    }②. 属性动画生存,  以setTranslationX为例,实在就是把值生存到一个变量中mRenderNode
View.java    final RenderNode mRenderNode;    public View(Context context) {          mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this));    }    public void setTranslationX(float translationX) {        if (translationX != getTranslationX()) {            mRenderNode.setTranslationX(translationX);            invalidateViewProperty(false, true);        }③. 绘制过程
View.javaboolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {        Transformation transformToApply = null;        //获取补间动画        final Animation a = getAnimation();        if (a != null) {            //根据时间, 盘算出必要的矩阵厘革信息,并用Transformation包起来            applyLegacyAnimation(parent, drawingTime, a, scalingRequired);            //这时transformToApply内里已经有变动矩阵的信息            transformToApply = parent.getChildTransformation();        }      //canvas 矩阵变动      if (transformToApply != null) {          canvas.concat(transformToApply.getMatrix());      }          //获取属性动画的矩阵厘革信息,canvas 矩阵变动      if (!childHasIdentityMatrix && !drawingWithRenderNode) {            canvas.concat(getMatrix());     }      //开始常见的draw绘制      draw(canvas);}④. 补间动画,怎样盘算Matrix()信息 (矩阵变动信息)
Matrix()信息, 现实上是包裹起来的,结构如下图:

View.javaboolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {    applyLegacyAnimation(parent, drawingTime, a, scalingRequired);}private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime, Animation a, boolean scalingRequired) {      Transformation t = parent.getChildTransformation();      a.getTransformation(drawingTime, t, 1f);}Animation.java    public boolean getTransformation(long currentTime, Transformation outTransformation,float scale) {        return getTransformation(currentTime, outTransformation);    }    public boolean getTransformation(long currentTime, Transformation outTransformation) {            //估值器处理惩罚, 不是本章重点            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);            applyTransformation(interpolatedTime, outTransformation);    }        //获取Matrix()信息,      protected void applyTransformation(float interpolatedTime, Transformation t) {    }可以看到, Animation类中具体的算法applyTransformation()是空实现, 必要交给子类来实现, 如果必要自界说动画算法, 关键是重写这个方法, 比方体系提供的平移动画:TranslateAnimation.java
    @Override    protected void applyTransformation(float interpolatedTime, Transformation t) {        float dx = mFromXDelta;        float dy = mFromYDelta;        if (mFromXDelta != mToXDelta) {            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);        }        if (mFromYDelta != mToYDelta) {            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);        }        t.getMatrix().setTranslate(dx, dy);    }⑤.  属性动画,怎样盘算Matrix()信息 (矩阵变动信息)
mRenderNode就是前面生存的属性动画信息,将matrix传到RenderNode举行赋值, 末了跑到native内里处理惩罚了,具体赋值的算法这里就不穷究了.
View.java    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {      canvas.concat(getMatrix());    }    public Matrix getMatrix() {        final Matrix matrix = mTransformationInfo.mMatrix;        mRenderNode.getMatrix(matrix);  // 传进去后, 在内里给matrix赋值        return matrix;    }  RenderNode.java    public void getMatrix(@NonNull Matrix outMatrix) {        nGetTransformMatrix(mNativeRenderNode, outMatrix.native_instance);    }      @CriticalNative    private static native void nGetTransformMatrix(long renderNode, long nativeMatrix);五. 动画是怎样做到一帧一帧运动的?


  • 每次draw()的时间,都是根据当前时间, 来获取动画对应的坐标.
  • 如果动画没到竣事时间,调用invalidate(),期待下一次垂直同步信号, 会继续实行draw(),  具体的可以去相识 屏幕渲染机制.
六. 补间动画 和 属性动画  对于点击触摸事件有什么差异?

动画只是位置上有所厘革, 以是我们只必要 事件 对于位置是怎样判定的就可以了,源码如下:
ViewGroup.javapublic boolean dispatchTouchEvent(MotionEvent ev) {    for (int i = childrenCount - 1; i >= 0; i--) {            View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);             //判定触摸的坐标 是否在child内             if (!isTransformedTouchPointInView(x, y, child, null)) {                  continue;             }    }}protected boolean isTransformedTouchPointInView(float x, float y, View child,PointF outLocalPoint) {    //关键:原始坐标+属性动画 ---> 新的坐标     transformPointToViewLocal(point, child);  //实行完这一句之后,point就已经算上动画后的坐标了    final boolean isInView = child.pointInView(point[0], point[1]);    return isInView;}    public void transformPointToViewLocal(float[] point, View child) {        if (!child.hasIdentityMatrix()) {            child.getInverseMatrix().mapPoints(point); //重新盘算point坐标        }    }View.java     //获取逆矩阵    public final Matrix getInverseMatrix() {        final Matrix matrix = mTransformationInfo.mInverseMatrix;        mRenderNode.getInverseMatrix(matrix);        return matrix;    } /*package*/ final boolean pointInView(float localX, float localY) {        return pointInView(localX, localY, 0);    }        //终极盘算位置    public boolean pointInView(float localX, float localY, float slop) {        return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&                localY < ((mBottom - mTop) + slop);    }结论: 事件处理惩罚盘算坐标的时间,只是把属性动画思量进去了, 并没有把补间动画算进去,  以是属性动画运动后,点击触摸事件是可以触发的,补间动画则不可.
思索: 如果补间动画也必要处理惩罚点击触摸事件, 那怎么办呢?能看懂本章内容的话, 那应该很好办理了.
以上内容, 仅代表个人观点, 如有差异意见, 接待指出一起讨论.
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-23 20:03, Processed in 0.181090 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表