关于v4包的Fragment过渡动画的事件监听无响应问题解决
2016-12-04 22:32
786 查看
项目中部分功能模块采用了单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方法:
setHWLayerAnimListenerIfAlpha,在这个方法中,我们看到当符合某些条件时,会对Animation动画重新设置事件监听,这样就会覆盖之前的设置。
通过对setHWLayerAnimListenerIfAlpha中重新设置动画监听代码的分析,不难看出,当shouldRunOnHWLayer检查当前view符合启用硬件加速条件时,会通过重新设置动画事件监听,来对Fragment过渡动画启用硬件加速优化。
那如何解决呢?shouldRunOnHWLayer中对符合硬件加速的第一个条件就是必须没有开启硬件加速,所以我的做法如下:
1、在onCreateAnimation中,设置动画监听事件之前,启用硬件加速,这样moveToState方法中就不会重新设置动画监听;
2、在设置动画的事件监听之前,获取是否符合启用硬件加速的条件,在onAnimationStart中,重新根据启用条件,决定继续开启还是关闭硬件加速,这样如果本来不需要开启,则在这里可以关闭;当然在onAnimationEnd中,如果开启了硬件加速一定要关闭;
通过以上处理,既能够自己监听事件动画,又没有失去硬件加速对过渡动画的优化。
我们知道,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); } });
相关文章推荐
- 对android里布局文件当中的TextView对象设置事件监听,但是不响应问题解决
- 解决ViewPager嵌套Fragment内部点击事件无响应问题
- cocos2d-x 3.x 关于cocostudio的UI将事件吃掉,场景Layer无法响应手势的问题解决方法
- 解决ViewPager嵌套Fragment内部点击事件无响应问题
- iOS,关于真机tableViewCell上面button响应事件,不同版本崩溃问题解决
- Android开发中关于如何解决Fragment中弹出键盘会把底部菜单栏顶上去的问题以及返回键的监听问题
- 关于flex中RichTextEditor控件,监听Ctrl+s事件和去除Ctrl+s按下时在RichTextEditor生成的不可知字符的问题
- iphone响应地图长按事件 解决长按响应两次的问题
- 关于MapControl和Map添加事件监听无效的问题
- Android 关于嵌套listView时onItemClick事件不响应的解决办法
- 关于Sql Server2000 1433端口本地不监听问题的解决
- 解决 点击ListView空白处事件不响应问题
- Android 关于嵌套listView时onItemClick事件不响应的解决办法
- 关于TreeView对添加海量节点的响应(续)-问题的完美解决
- symbian 关于通讯录监听的问题解决
- 关于在自定义控件中事件不响应的问题
- 关于锚点页内链接跳转出现问题(不响应,没有反应)的解决方法(ZT)
- 关于一个view焦点转移时响应按键事件的问题
- 关于写ATL控件时对控件添加事件事 'IID__IXXXEvents' : undeclared identifier的问题解决方法
- 解决 点击ListView空白处事件不响应问题