项目中部分功能模块采用了单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);
}
});

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

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

    主题是关于动画的,但是不是什么动画的内容都包括.先泛泛的介绍一下,然后详细的介绍一下翻代码找见的一个好玩的动画的使用.以下的内容包括Android 3和Android 3.1等引入的API,在使用中请 ...

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

    //下面的例子证明 fadeIn() fadeOut() 不能使用transitionend事件进行监听. //说白了在fadeIn fadeOut 后面监听动画结束时,transitionend是不 ...

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

    Android5.0之后为我们提供了许多炫酷的界面过渡效果,其中共享元素过渡也是很有亮点的一个效果,但这个效果只能在Android5.0之后使用,那今天我们就来将共享元素过渡效果兼容到Android4 ...

  4. [Android]Fragment自定义动画、动画监听以及兼容性包使用

    Fragment是Android在API 11之后加入的一个组件,对提高Android开发中的布局合理性和布局效率都有很大作用,尤其是在Android平板等大屏幕设备的开发中,Fragment的引入能 ...

  5. Fragment用app包还是v4包解析

    转自:http://blog.csdn.net/zc0908/article/details/50721553 1)问题简述 相信很多的朋友在调用Fragment都会遇到下面的情况: 这个时候问题来了 ...

  6. Android做法说明(3)---Fragment使用app袋或v4包解析

    Android做法说明(3)---Fragment使用app袋或v4包解析 1)问题简述 相信非常多的朋友在调用Fragment都会遇到以下的情况: watermark/2/text/aHR0cDov ...

  7. ViewPager和Fragment组合 v4包下的页面切换

    /* *主页面下 */ //-------------主页面下---------------------- package com.example.viewpagerfragment; import ...

  8. 14 Fragment的V4包的使用

    activity_main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android ...

  9. Android编程之Fragment使用动画造成Unknown animation name: objectAnimator异常

    在为Fragment做切换动画.启动后遇到了一个异常: Caused by: java.lang.RuntimeException: Unknown animation name: objectAni ...

随机推荐

  1. 四则运算 SPEC 20160911

    本文档随时可能修改,并且没有另行通知. 请确保每一次在开始修改你的代码前,读标题中的日期,如果晚于你上次阅读, 请重读一次. 教师节你去探望初中数学老师,她感叹你当年真是个优秀学生啊,从来不报怨作 业 ...

  2. PHP 爬虫——QueryList

    前言: 来了个任务说要做个电影网站,要写个壳,数据直接从别人那扒.行吧!那就要学习下PHP爬虫了.占个博客,以后补充.http://study.querylist.cc/archives/6/ 之前开 ...

  3. 『编程题全队』Alpha 阶段冲刺博客Day7

    1.每日站立式会议 1.会议照片 2.昨天已完成的工作统计 孙志威: 1.添加了网络通信管理类 2.稍微修改了燃尽图模块ChartWidget 3.在主窗口中添加了用户信息框 4.重构了项目中的文件结 ...

  4. Linux命令(十) 在文件或目录之间创建链接 ln

    命令简介 ln 命令用于连接文件或目录,如同时指定两个以上的文件或目录,且最后的目的地是一个已经存在的目录,则会把前面指定的所有文件或目录复制到该目录中.若同时指定多个文件或目录,且最后的目的地是一个 ...

  5. linux 开机直接进入命令行

    一.图形界面和命令行切换 linux系统在启动图形化界面后,可以在图形界面和命令行之间来回切换,linux提供了6个命令行终端(terminal或Console),分别为tty1——tty6,使用Ct ...

  6. c++中冒号(:)和双冒号(::)的用法

    1.冒号(:)的用法 (1)表示机构内位域的定义(即该变量占几个bit空间) typedef struct _XXX{ unsigned char a:4; unsigned char c; } ; ...

  7. ethereum & ETC

    ethereum & ETC https://github.com/ethereum/go-ethereum https://discountry.github.io/tutorial/201 ...

  8. 用宏实现C/C++从非零整数开始的数组

    相信大家在刚学习C/C++时,都会对数组下标从0开始编号有疑惑.尽管我是喜欢从0开始编号的“0党”,但是也有很多的人是喜欢从1开始编号. 意识到C/C++数组与指针具有一定的相似性后,我开始构思如何仿 ...

  9. 【刷题】BZOJ 1124 [POI2008]枪战Maf

    Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人也不同. ...

  10. 【bzoj1069】最大土地面积

    Description 在某块平面土地上有N个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成的多边形面积最大. Input 第1行一个正整数N,接下来N行,每行2个数x,y ...