项目中部分功能模块采用了单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. Linux内核实验作业四

    实验作业:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 20135313吴子怡.北京电子科技学院 [第一部分]使用库函数API来获取用户标识号.库函数为getuid() 代码如下: ...

  2. 20135327郭皓——Linux内核分析第二周 操作系统是如何工作的

    操作系统是如何工作的 上章重点回顾: 计算机是如何工作的?(总结)——三个法宝 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的 ...

  3. 20190215面试-C#操作外设-多线程-shocket

    百度了下,ic卡读卡器 文章;C# 读IC卡程序这个文章还不错. 从北京金木雨电子有限公司下载了,兼容IC卡 身份证阅读器的SDK资料,里面有介绍如何连接ic读卡器,对卡进行一些操作. MasterR ...

  4. A brief introduction of myself

    来到博客园的第二天.晚上天色墨蓝,余热未退,北京这里的秋风干松爽利,和小组的伙伴们吃了一顿饱饱的香锅,按照咱们国人的传统,所有的事情在饭桌上都解决了,包括队员之间相互的认识和短期任务的分配以及后期的团 ...

  5. VS2013安装及测试

    一.Visual Studio的安装 首先是Visual Studio英文版的安装,安装完成后,为了用的时候方便,我从官网下载Visual Studio 2013的语言包并安装. 二.进行单元测试. ...

  6. ElasticSearch 2 (25) - 语言处理系列之同义词

    ElasticSearch 2 (25) - 语言处理系列之同义词 摘要 词干提取有助于通过简化屈折词到它们词根的形式来扩展搜索的范围,而同义词是通过关联概念和想法来扩展搜索范围的.或许没有文档能与查 ...

  7. 深入理解C++中的异常处理机制

    异常处理 增强错误恢复能力是提高代码健壮性的最有力的途径之一,C语言中采用的错误处理方法被认为是紧耦合的,函数的使用者必须在非常靠近函数调用的地方编 写错误处理代码,这样会使得其变得笨拙和难以使用.C ...

  8. IntelliJ IDEA中文乱码问题

    转自  https://blog.csdn.net/m0_37893932/article/details/78280663 1 file->settings->appearence里面有 ...

  9. 新版 Chrome Ajax 跨域调试

    一.前言 web 开发中 Ajax 是十分常见的技术,但是在前后端使用接口对接的调试过程中不可避免会碰到跨域问题.今天我给大家介绍一个十分简单有效的方法. 跨域经典错误 二.Chrome 跨域设置 首 ...

  10. asp.net使用动态模版导出word

    具体思路: 1.先制作Word模版,使用文本框+书签的方式来设计模版: 2.模版制作完之后,根据模版生成新文件,使用File.Copy方法,生成.doc格式新文件: 3.后台取得数据,参照网页渲染的方 ...