项目中部分功能模块采用了单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. 1080. Graduate Admission (30)-排序

    先对学生们进行排序,并且求出对应排名. 对于每一个学生,按照志愿的顺序: 1.如果学校名额没满,那么便被该学校录取,并且另vis[s][app[i].ranks]=1,表示学校s录取了该排名位置的学生 ...

  2. PHP学习笔记2

    PHP Switch语句 用于根据多个不同条件执行不同动作.如果不在每个条件后加break,将会输出所有结果. <?php $language="java"; switch( ...

  3. Fibbing以让虚结点的设置更简单为目的优化网络需求

  4. 第一个spring,第一天。

    陈志棚:界面跳转与框架 李天麟:游戏界面ui 徐侃:算法代码的设计 经过热烈的讨论后,我们各自在完成自己的任务.

  5. 团队作业(五)-笔记app top5

    在互联网快速发展的情况下,各个行业的软件层出不穷,五花八门.各个行业都有相当多的软件介入其中,在如此多的软件之中,便有了相当激烈的竞争角逐.今天我们十五万的总冠军就着笔记APP行业中位列top 5的软 ...

  6. final发布评论Ⅱ

    奋斗吧兄弟:奋斗吧兄弟团队提供了草原.水域.田地三种环境可以进行选择.功能如下:添加自定义生物功能可以增加生物种类,但是无法设置捕食关系,这里希望想办法完善.生物除了图片方式以外,还可以以文字的方式展 ...

  7. Linux用户管理机制

    Linux系统中的用户管理涉及用户账号文件 /etc/passwd.用户密码文件 /etc/shadow.用户组文件 /etc/group. 一.用户账号文件 /etc/passwd 该文件为纯文本文 ...

  8. [阮一峰]Linux 守护进程的启动方法

    "守护进程"(daemon)就是一直在后台运行的进程(daemon). 本文介绍如何将一个 Web 应用,启动为守护进程. 一.问题的由来 Web应用写好后,下一件事就是启动,让它 ...

  9. 查看Jira 使用的H2数据库 数据结构以及内容的方法

    1. 同事在研究jira 想看看jira的数据库 数据结构, 告知使用的是java的H2数据库. 如图示 2. 然后根据此内容 进行百度等. 下载 可以进行数据库连接的工具,主要找到两个,下载地址分别 ...

  10. Rain on your Parade HDU - 2389 (hc板题)

    在客人能够拿到的伞与客人之间建边  跑hc就好了.... 看看别人的:https://blog.csdn.net/wall_f/article/details/8248350 #include < ...