关于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);
}
});
关于v4包的Fragment过渡动画的事件监听无响应问题解决的更多相关文章
- 属性动画和Activity、Fragment过渡动画等
主题是关于动画的,但是不是什么动画的内容都包括.先泛泛的介绍一下,然后详细的介绍一下翻代码找见的一个好玩的动画的使用.以下的内容包括Android 3和Android 3.1等引入的API,在使用中请 ...
- transitionend事件 监听 fadeIn fadeOut 两个方法无效(动画结束时无法执行transitionend里面的代码)
//下面的例子证明 fadeIn() fadeOut() 不能使用transitionend事件进行监听. //说白了在fadeIn fadeOut 后面监听动画结束时,transitionend是不 ...
- 实现Android5.0过渡动画兼容库
Android5.0之后为我们提供了许多炫酷的界面过渡效果,其中共享元素过渡也是很有亮点的一个效果,但这个效果只能在Android5.0之后使用,那今天我们就来将共享元素过渡效果兼容到Android4 ...
- [Android]Fragment自定义动画、动画监听以及兼容性包使用
Fragment是Android在API 11之后加入的一个组件,对提高Android开发中的布局合理性和布局效率都有很大作用,尤其是在Android平板等大屏幕设备的开发中,Fragment的引入能 ...
- Fragment用app包还是v4包解析
转自:http://blog.csdn.net/zc0908/article/details/50721553 1)问题简述 相信很多的朋友在调用Fragment都会遇到下面的情况: 这个时候问题来了 ...
- Android做法说明(3)---Fragment使用app袋或v4包解析
Android做法说明(3)---Fragment使用app袋或v4包解析 1)问题简述 相信非常多的朋友在调用Fragment都会遇到以下的情况: watermark/2/text/aHR0cDov ...
- ViewPager和Fragment组合 v4包下的页面切换
/* *主页面下 */ //-------------主页面下---------------------- package com.example.viewpagerfragment; import ...
- 14 Fragment的V4包的使用
activity_main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android ...
- Android编程之Fragment使用动画造成Unknown animation name: objectAnimator异常
在为Fragment做切换动画.启动后遇到了一个异常: Caused by: java.lang.RuntimeException: Unknown animation name: objectAni ...
随机推荐
- Daily Scrum - 11/17
今天小组例会内容较少.拜重阳将一个简易的UI设计好push上TFS了,其他人没有太多进展.在原有项目基础上继续开发看似工作量变少,其实开始需要弄清楚原先代码的实现架构和各种借口还是比较困难的.
- Activiti End Event及其派生类使用范例
http://tynerblain.com/blog/2006/08/11/bpmn-end-events-1/ 普通流程完结就用end event,而比如无效卡支付时,就会进入cancel end ...
- 如何判断可见字符 Unicode
一个Unicode字符串,如何判断其中都是可见字符? //根据国标 GB2312 的中文汉字及符号 区位码的范围判断 Function CheckIsGB2312(Char : WideChar) : ...
- jira 插件介绍地址
1. 官方的 介绍地址 http://confluence.gjingao.com/pages/viewpage.action?pageId=328170 序号 插件名称 功能概要 供应商 资源 10 ...
- 【转】Mysql事务,并发问题,锁机制
转自:http://www.cnblogs.com/fidelQuan/p/4549068.html 1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成 ...
- 牛客网暑期ACM多校训练营(第二场)J farm (二维树状数组)
题目链接: https://www.nowcoder.com/acm/contest/140/J 思路: 都写在代码注释里了,非常好懂.. for_each函数可以去看一下,遍历起vector数组比较 ...
- 【Treap 例题】神秘岛(island)
神秘岛(island) 题目描述: 除了敲代码和撩妹,旅行是cxw123 的第三爱好.他来到了澳大利亚东北部的大宝礁,在这里,有一个隔绝人世的神秘岛,这个岛不同于附近其他的珊瑚岛,它的生长速度极快,甚 ...
- GDOI2018 Day1 题目总结
T1:农场 题意:有一个长为 $n$ 的序列 $a$,要求将其分成尽可能多的部分,使得每一部分的 $a_i$ 的和相等.求最多能分成的部分数. $30\%:1\le n\le 1000$ $80\%: ...
- 滥用基于资源约束委派来攻击Active Directory
0x00 前言 早在2018年3月前,我就开始了一场毫无意义的争论,以证明TrustedToAuthForDelegation属性是无意义的,并且可以在没有该属性的情况下实现“协议转换”.我相信,只要 ...
- MySQL的COUNT()函数理解
MySQL的COUNT()函数理解 标签(空格分隔): MySQL5.7 COUNT()函数 探讨 写在前面的话 细心的朋友会在平时工作和学习中,可以看到MySQL的COUNT()函数有多种不同的参数 ...