补间动画移动后,点击事件的响应为什么还在原来的位置?

那今天我们就来从源码解析原理
补间动画可以在一个视图容器内执行一系列简单变换(具体的变换步骤有:位置、大小、旋转、透明度);
我们可以通过平移、旋转、缩放、透明度等API进行具体的操作;
补间动画的实现方式可以通过 XML或通过Android代码两种方式 去定义;
文件名:animator_translate.xml
- xmlns:android="http://schemas.android.com/apk/res/android"
 - android:fromXDelta="0"
 - android:fromYDelta="0"
 - android:toYDelta="0"
 - android:toXDelta="200"
 - android:duration="500"
 - android:fillAfter="true">
 
代码加载xml文件获取动画
- //加载动画
 - Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_translate);
 - //执行动画
 - testBtn.startAnimation(animation);
 
- TranslateAnimation translateAnimation = new TranslateAnimation(0,200,0,0);
 - translateAnimation.setDuration(500);//动画执行时间
 - translateAnimation.setFillAfter(true);//动画执行完成后保持状态
 - //执行动画
 - testBtn.startAnimation(translateAnimation);
 
startAnimation(rotateAnimation)方法进入源码;
- //View.java
 - public void startAnimation(Animation animation) {
 - animation.setStartTime(Animation.START_ON_FIRST_FRAME);
 - setAnimation(animation);
 - invalidateParentCaches();
 - invalidate(true);
 - }
 
首先是通过setStartTime()设置了动画的开始时间;
- //View.java
 - public void setStartTime(long startTimeMillis) {
 - mStartTime = startTimeMillis;
 - mStarted = mEnded = false;
 - mCycleFlip = false;
 - mRepeated = 0;
 - mMore = true;
 - }
 
这里只是对一些变量进行赋值,再来看看下一个方法;
设置动画setAnimation(animation):
- //View.java
 - public void setAnimation(Animation animation) {
 - mCurrentAnimation = animation;
 - if (animation != null) {
 - if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF
 - && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
 - animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
 - }
 - animation.reset();
 - }
 - }
 
这里面也是将动画实例赋值给当前的成员变量;
分析startAnimation()方法里的invalidateParentCaches();
- //View.java
 - protected void invalidateParentCaches()
 - if (mParent instanceof View) {
 - ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;
 - }
 - }
 
可以看到这里仅仅是设置动画标记,在视图构建或者属性改变时是必要的;
再回到startAnimation()方法里面invalidate(true);
- //View.java
 - public void invalidate(boolean invalidateCache) {
 - invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
 - }
 - void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
 - boolean fullInvalidate) {
 - if (mGhostView != null) {
 - mGhostView.invalidate(true);
 - return;
 - }
 - .................
 - // Propagate the damage rectangle to the parent view.
 - final AttachInfo ai = mAttachInfo;
 - final ViewParent p = mParent;
 - if (p != null && ai != null && l < r && t < b) {
 - final Rect damage = ai.mTmpInvalRect;
 - damage.set(l, t, r, b);
 - p.invalidateChild(this, damage);
 - }
 - }
 - }
 
这里着重看p.invalidateChild(this, damage);
- //ViewGroup.java
 - @Deprecated
 - @Override
 - public final void invalidateChild(View child, final Rect dirty) {
 - final AttachInfo attachInfo = mAttachInfo;
 - if (attachInfo != null && attachInfo.mHardwareAccelerated) {
 - // HW accelerated fast path
 - onDescendantInvalidated(child, child);
 - return;
 - }
 - ViewParent parent = this;
 - .........
 - do {
 - View view = null;
 - if (parent instanceof View) {
 - view = (View) parent;
 - }
 - .........
 - parent = parent.invalidateChildInParent(location, dirty);
 - } while (parent != null);
 - }
 - }
 
- //ViewGroup.java
 - @Deprecated
 - @Override
 - public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
 - if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
 - .......
 - return mParent;
 - }
 - return null;
 - }
 
- //ViewRootImpl.java
 - @Override
 - public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
 - checkThread();
 - if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
 - if (dirty == null) {
 - invalidate();
 - return null;
 - } else if (dirty.isEmpty() && !mIsAnimating) {
 - return null;
 - }
 - .......
 - invalidateRectOnScreen(dirty);
 - return null;
 - }
 
这里所有的返回值都变为null了,之前执行的do{}while()循坏也会停止。
- //ViewRootImpl.java
 - private void invalidateRectOnScreen(Rect dirty) {
 - ......
 - if (!mWillDrawSoon && (intersected || mIsAnimating)) {
 - scheduleTraversals();
 - }
 - }
 - //ViewRootImpl.java
 - void scheduleTraversals() {
 - if (!mTraversalScheduled) {
 - mTraversalScheduled = true;
 - mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
 - mChoreographer.postCallback(
 - Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
 - if (!mUnbufferedInputDispatch) {
 - scheduleConsumeBatchedInput();
 - }
 - notifyRendererOfFramePending();
 - pokeDrawLockIfNeeded();
 - }
 - }
 
主要看mTraversalRunnable,我们找到mTraversalRunnable这个类;
- //ViewRootImpl.java
 - final class TraversalRunnable implements Runnable {
 - @Override
 - public void run() {
 - doTraversal();
 - }
 - }
 - final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
 
doTraversal()方法;
- //ViewRootImpl.java
 - void doTraversal() {
 - .......
 - performTraversals();
 - .......
 - }
 - }
 
- boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
 - ...
 - //清除上次动画保存的Transformation
 - if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) {
 - parent.getChildTransformation().clear();
 - parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;
 - }
 - ......
 - final Animation a = getAnimation();
 - if (a != null) {
 - //根据当前时间计算当前帧的动画,more表示是否需要执行更多帧的动画
 - more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
 - concatMatrix = a.willChangeTransformationMatrix();
 - if (concatMatrix) {
 - mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
 - }
 - //拿到当前帧需要的变换 ,这个值会在applyLegacyAnimation中进行设置
 - transformToApply = parent.getChildTransformation();
 - }
 - ....
 - if (transformToApply != null) {
 - if (concatMatrix) {
 - if (drawingWithRenderNode) {
 - renderNode.setAnimationMatrix(transformToApply.getMatrix());
 - } else {
 - // Undo the scroll translation, apply the transformation matrix,
 - // then redo the scroll translate to get the correct result.
 - canvas.translate(-transX, -transY);
 - canvas.concat(transformToApply.getMatrix());//在这里调用canvas的concat方法,实现最终的平移效果 (做矩阵相乘)
 - canvas.translate(transX, transY);
 - }
 - //标记需要清除Tranformation
 - parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
 - }
 - float transformAlpha = transformToApply.getAlpha();
 - if (transformAlpha < 1) {
 - alpha *= transformAlpha;
 - parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
 - }
 - }
 - ...
 - }
 
- private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
 - Animation a, boolean scalingRequired) {
 - ...
 - //获取Transformation 每个ViewGroup中的子View共同使用一个Transformation 为了多个View有动画时频繁创建多个Transformation
 - //这个和在draw方法中取出的transformToApply是一个对象 就是最终应用到Canvas上的Transform
 - final Transformation t = parent.getChildTransformation();
 - //调用Animation的getTransformation方法来根据当前时间计算Transformation 这个对象的值最终会由getTransformation方法中进行赋值
 - boolean more = a.getTransformation(drawingTime, t, 1f);
 - invalidationTransform = t;
 - ...
 - //如果动画还没有播放完成 需要让动画循环起来 实际上是继续调用invalidate
 - if (more) {
 - if (parent.mInvalidateRegion == null) {
 - parent.mInvalidateRegion = new RectF();
 - }
 - final RectF region = parent.mInvalidateRegion;
 - //调用Animation 的getInvalidateRegion来根据invalidationTransform计算 parent的invalidateRegion
 - a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
 - invalidationTransform);
 - // The child need to draw an animation, potentially offscreen, so
 - // make sure we do not cancel invalidate requests
 - parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
 - final int left = mLeft + (int) region.left;
 - final int top = mTop + (int) region.top;
 - //调用invalidate执行下一次绘制请求,这样动画就动起来了
 - parent.invalidate(left, top, left + (int) (region.width() + .5f),
 - top + (int) (region.height() + .5f));
 - }
 - }
 
- //Animation.java
 - //返回值表示动画是否没有播放完成 并且需要计算outTransformation 也就是动画需要做的变化
 - public boolean getTransformation(long currentTime, Transformation outTransformation) {
 - if (mStartTime == -1) {
 - mStartTime = currentTime;//记录第一帧的时间
 - }
 - if (duration != 0) {
 - normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / //计算运行的进度(0-1) (当前时间-开始时间+偏移量)/动画总时长
 - (float) duration;
 - }
 - final boolean expired = normalizedTime >= 1.0f || isCanceled(); //判断动画是否播放完成 或者被取消
 - mMore = !expired;
 - if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); //处理最大值
 - final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);//根据插值器计算的当前动画运行进度
 - applyTransformation(interpolatedTime, outTransformation);//根据动画进度 计算最终的outTransformation
 - return mMore;
 - }
 
- //applyTransformation每种类型的动画都有自己的实现 这里以位移动画为例
 - //TranslateAnimation.java
 - @Override
 - protected void applyTransformation(float interpolatedTime, Transformation t) {
 - //Transformation可以理解成 存储View的一些变换信息,将变化信息保存到成员变量matrix中
 - float dx = mFromXDelta;
 - float dy = mFromYDelta;
 - if (mFromXDelta != mToXDelta) {
 - dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);//计算X方向需要移动的距离
 - }
 - if (mFromYDelta != mToYDelta) {
 - dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);//计算Y方向需要移动的距离
 - }
 - t.getMatrix().setTranslate(dx, dy); //将最终的结果设置到Matrix上面去
 - }
 
至于未来会怎样,要走下去才知道,反正路还很长,天总会亮;
加油老铁们!
本文转载自微信公众号「Android开发编程」
                文章题目:补间动画源码中分析机制原理
                
                文章分享:http://www.csdahua.cn/qtweb/news21/328921.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网