关于v4包的Fragment过渡动画的事件监听无响应问题解决

  项目中部分功能模块采用了单Activity+多Fragment模式,当Fragment切换时,需要在过渡动画执行完后做一些操作,通常就是在自己封装的FragmentBase中重写onCreateAnimation方法,创建一个Animation对象,并添加动画的事件监听。而最近升级了v4包后,突然发现添加的动画事件监听无响应了。通过查看源码,发现在v4包中关于Fragment管理类FragmentManagerImpl中,在获取Animation对象后,也添加了对动画的监听事件,也就覆盖了我自己在onCreateAnimtion方法中对Animation动画的事件监听。

  我们知道,Fragment生命周期不同阶段的处理主要在android.support.v4.app.FragmentManagerImpl.moveToState方法中,而如下代码则是当Fragment第一次加载时截取的部分代码,其中我们看到在执行performCreateView方法以后,有一个对loadAnimation方法的调用,这个方法会执行我们在FragmentBase中实现的onCreateAnimation方法,并返回Animation对象,而获取到Animation对象后,调用了setHWLayerAnimListenerIfAlpha方法。

FragmentManagerImpl的moveToState方法:

case 1:
                if(newState > 1) {
                    if(DEBUG) {
                        Log.v("FragmentManager", "moveto ACTIVITY_CREATED: " + f);
                    }

                    if(!f.mFromLayout) {
                        ViewGroup v = null;
                        if(f.mContainerId != 0) {
                            v = (ViewGroup)this.mContainer.onFindViewById(f.mContainerId);
                            if(v == null && !f.mRestored) {
                                this.throwException(new IllegalArgumentException("No view found for id 0x" + Integer.toHexString(f.mContainerId) + " (" + f.getResources().getResourceName(f.mContainerId) + ") for fragment " + f));
                            }
                        }

                        f.mContainer = v;
                        f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), v, f.mSavedFragmentState);
                        if(f.mView != null) {
                            f.mInnerView = f.mView;
                            if(VERSION.SDK_INT >= 11) {
                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
                            } else {
                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                            }

                            if(v != null) {
                                Animation fragment = this.loadAnimation(f, transit, true, transitionStyle);
                                if(fragment != null) {
                                    this.setHWLayerAnimListenerIfAlpha(f.mView, fragment);
                                    f.mView.startAnimation(fragment);
                                }

                                v.addView(f.mView);
                            }

                            if(f.mHidden) {
                                f.mView.setVisibility(8);
                            }

                            f.onViewCreated(f.mView, f.mSavedFragmentState);
                        } else {
                            f.mInnerView = null;
                        }
                    }

                    f.performActivityCreated(f.mSavedFragmentState);
                    if(f.mView != null) {
                        f.restoreViewState(f.mSavedFragmentState);
                    }

                    f.mSavedFragmentState = null;
                }

  setHWLayerAnimListenerIfAlpha,在这个方法中,我们看到当符合某些条件时,会对Animation动画重新设置事件监听,这样就会覆盖之前的设置。

private void setHWLayerAnimListenerIfAlpha(View v, Animation anim) {
        if(v != null && anim != null) {
            if(shouldRunOnHWLayer(v, anim)) {
                anim.setAnimationListener(new FragmentManagerImpl.AnimateOnHWLayerIfNeededListener(v, anim));
            }

        }
    }
static boolean shouldRunOnHWLayer(View v, Animation anim) {
        return ViewCompat.getLayerType(v) == 0 && ViewCompat.hasOverlappingRendering(v) && modifiesAlpha(anim);
    }

    static boolean modifiesAlpha(Animation anim) {
        if(anim instanceof AlphaAnimation) {
            return true;
        } else {
            if(anim instanceof AnimationSet) {
                List anims = ((AnimationSet)anim).getAnimations();

                for(int i = 0; i < anims.size(); ++i) {
                    if(anims.get(i) instanceof AlphaAnimation) {
                        return true;
                    }
                }
            }

            return false;
        }
    }

  

static class AnimateOnHWLayerIfNeededListener implements AnimationListener {
        private boolean mShouldRunOnHWLayer = false;
        private View mView;

        public AnimateOnHWLayerIfNeededListener(View v, Animation anim) {
            if(v != null && anim != null) {
                this.mView = v;
            }
        }

        @CallSuper
        public void onAnimationStart(Animation animation) {
            this.mShouldRunOnHWLayer = FragmentManagerImpl.shouldRunOnHWLayer(this.mView, animation);
            if(this.mShouldRunOnHWLayer) {
                this.mView.post(new Runnable() {
                    public void run() {
                        ViewCompat.setLayerType(AnimateOnHWLayerIfNeededListener.this.mView, 2, (Paint)null);
                    }
                });
            }

        }

        @CallSuper
        public void onAnimationEnd(Animation animation) {
            if(this.mShouldRunOnHWLayer) {
                this.mView.post(new Runnable() {
                    public void run() {
                        ViewCompat.setLayerType(AnimateOnHWLayerIfNeededListener.this.mView, 0, (Paint)null);
                    }
                });
            }

        }

        public void onAnimationRepeat(Animation animation) {
        }
    }

  通过对setHWLayerAnimListenerIfAlpha中重新设置动画监听代码的分析,不难看出,当shouldRunOnHWLayer检查当前view符合启用硬件加速条件时,会通过重新设置动画事件监听,来对Fragment过渡动画启用硬件加速优化。

  那如何解决呢?shouldRunOnHWLayer中对符合硬件加速的第一个条件就是必须没有开启硬件加速,所以我的做法如下:

  1、在onCreateAnimation中,设置动画监听事件之前,启用硬件加速,这样moveToState方法中就不会重新设置动画监听;

  2、在设置动画的事件监听之前,获取是否符合启用硬件加速的条件,在onAnimationStart中,重新根据启用条件,决定继续开启还是关闭硬件加速,这样如果本来不需要开启,则在这里可以关闭;当然在onAnimationEnd中,如果开启了硬件加速一定要关闭;

  通过以上处理,既能够自己监听事件动画,又没有失去硬件加速对过渡动画的优化。

// 是否符合启用硬件加速需要
            final boolean hardwareState = shouldRunOnHWLayer(getView(), anim);
            // 启用硬件加速避免FragmentManagerImpl中重置了setAnimationListener
            getView().setLayerType(View.LAYER_TYPE_HARDWARE, null);

            anim.setAnimationListener(new Animation.AnimationListener()
            {
                public void onAnimationStart(Animation animation)
                {
                    // 开启硬件加速优化
                    if (getView() != null)
                    {
                        getView().post(new Runnable()
                        {
                            public void run()
                            {
                                ViewCompat.setLayerType(getView(), hardwareState ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, (Paint) null);
                            }
                        });
                    }
                    performTransitionAnimationStart(enter);
                }

                public void onAnimationRepeat(Animation animation)
                {
                }

                public void onAnimationEnd(Animation animation)
                {
                    // 关闭硬件加速
                    if (getView() != null && hardwareState)
                    {
                        getView().post(new Runnable()
                        {
                            public void run()
                            {
                                ViewCompat.setLayerType(getView(), View.LAYER_TYPE_NONE, (Paint) null);
                            }
                        });
                    }
                    performTransitionAnimationEnd(enter);
                }
            });
时间: 2024-10-31 14:30:38

关于v4包的Fragment过渡动画的事件监听无响应问题解决的相关文章

css3动画的事件监听

1. -webkit-animation动画其实有三个事件: 开始事件 webkitAnimationStart 结束事件 webkitAnimationEnd 重复运动事件 webkitAnimationIteration 2.  css3的过渡属性transition,在动画结束时,也存在结束的事件:webkitTransitionEnd; 注意:transition,也仅仅有这一个事件. demo:点击查看demo (注:以上demo打开后,打开调试器查看) 以上事件是针对webkit内核

属性动画和Activity、Fragment过渡动画等

主题是关于动画的,但是不是什么动画的内容都包括.先泛泛的介绍一下,然后详细的介绍一下翻代码找见的一个好玩的动画的使用.以下的内容包括Android 3和Android 3.1等引入的API,在使用中请注意版本. 代码都是用Kotlin写的.如果你用的是新版的Android Studio. 创建项目之后,按下快捷键Shift+Ctrl+Alt+K就会自动把代码从java转换成Kotlin. 之后按照说明给项目配置Kotlin的插件即可.很简单.Kotlin的官网在这里:http://kotlinl

SSIS常用的包—WMI数据读取任务和WMI事件监听任务

Windows Management Instrumentation (WMI)是Windows的最高机密之一,它允许你通过一个脚本界面来管理Windows平台.WMI数据读取任务通过WQL语言(WMA专用语言)来 接触管理服务器或者工作平台(例如查看系统日志).查询语句可以将结果输出到变量中.WMI数据读取任务可以完成下面的工作. 在事务日志中查询可能的错误信息 查询正在运行的应用程序 查询在包运行过程中有多少可用RAM 判断有多少可用的磁盘空间 在编辑界面中可以配置WMI数据读取任务,如图3

transitionend事件 监听 fadeIn fadeOut 两个方法无效(动画结束时无法执行transitionend里面的代码)

//下面的例子证明 fadeIn() fadeOut() 不能使用transitionend事件进行监听. //说白了在fadeIn fadeOut 后面监听动画结束时,transitionend是不会执行的. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <st

jquery 解决事件监听动画延迟触发的问题

上代码 <div id="small"></div> <div id="large"></div> #small{ width: 100px; height: 100px; background: red; position: absolute; top: 100px; left: 100px; } #large{ width: 200px; height: 200px; background: purple; pos

android Fragment 实例 Listfragment listiew 下拉刷新 上拉加载 横向滑动事件监听

Android Fragment开发实例及他色功能说明 代码太长,请网盘下载附件源代码: http://yunpan.cn/cs24a2Z7C5kRk (提取码:a0cf)  有疑问的欢迎联系QQ972910164 一. CustomListView说明:可下拉刷新,上拉加载,横向滑动接口回调, /** * 功能类似ListView,天机以下特性: * 1. 可下拉刷新,上拉加载,实现CustomListView.OnPullListener接口 * 3. Slide切换,实现CustomLis

实现Android5.0过渡动画兼容库

Android5.0之后为我们提供了许多炫酷的界面过渡效果,其中共享元素过渡也是很有亮点的一个效果,但这个效果只能在Android5.0之后使用,那今天我们就来将共享元素过渡效果兼容到Android4.0,让5.0之前的手机也可以体验这么炫酷的效果吧. A transition animation compatible Library. 兼容Android5.0之后转场动画至Android4.0. github地址:https://github.com/zhangke3016/Translati

Android实习札记(3)---Fragment用app包还是v4包解析

Android实习札记(3)---Fragment用app包还是v4包解析 1)问题简述 相信很多的朋友在调用Fragment都会遇到下面的情况: 这个时候问题来了... 到底是用android.app下的Fragment还是用的android.support.v4.app包下的Fragment? 答:其实都可以,没错,都可以!!!我们都知道Fragment是3.0(API 11)后引入的 那么如果开发的app需要在3.0以下的版本运行呢?比如还有一点点市场份额的2.3! 于是乎,v4包就这样应

Fragment用app包还是v4包解析

转自:http://blog.csdn.net/zc0908/article/details/50721553 1)问题简述 相信很多的朋友在调用Fragment都会遇到下面的情况: 这个时候问题来了... 到底是用Android.app下的Fragment还是用的android.support.v4.app包下的Fragment? 答:其实都可以,没错,都可以!!!我们都知道Fragment是3.0(API 11)后引入的 那么如果开发的app需要在3.0以下的版本运行呢?比如还有一点点市场份