关于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 ...
随机推荐
- LeetCode 463. Island Perimeter岛屿的周长 (C++)
题目: You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 repr ...
- OS X(10.10) python3.4 matplotlib的安装
最近在用python做一些数据处理相关的工作,当然少不了matplotlib这个模块.之前在windows下分分钟安装成功,结果到了mac下死活编译不过去. 最后还是在stackoverflow上找到 ...
- 团队作业五之旅游行业手机APP分析
深入分析旅游业手机APP——分析员王奕 在接到组长分配的任务的时候,我的内心是激动的.因为自己不擅长编程,所以还是比较喜欢这种“外围”的文字工作.但是,面对数量庞大的旅游业APP,一时间自己真的不知 ...
- 关于给C盘扩容以及动态磁盘
2019.4.5 周五 磁盘分为基本磁盘和动态磁盘 基本磁盘转动态磁盘是很容易的 https://jingyan.baidu.com/article/59a015e3a3d504f7948865b1. ...
- ehlib使用内存表的方法
ehlib提供了一个TMemTableEh控件,这个控件不需要连接数据库就可以在ehlib中显示数据,在做一些虚的表格时比较有用. 简单的使用主要有这几个步骤: 1.添加量过控件Tdatasource ...
- vsphere web client 使用中文的解决办法
1. 很多网站这么说的: vsphere web client的默认URL为:https://hostname:9443/vsphere-client 可以在URL后面加上一个参数来指定区域语言 英语 ...
- 基于 Laravel 的 文件管理
以 laravel 5.5 为例,框架集成了文件系统和云存储功能 可以实现文件夹列表.创建.重命名.删除,文件列表.上传.重命名.删除等操作 一.先进行配置 在 config 文件夹下有 filesy ...
- 2013长春网赛 1006 hdu 4764 Stone(巴什博弈)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4764 题意:Tang 和 Jiang 玩一个游戏,轮流写下一个数,Tang先手,第一次Tang只能写[ ...
- 窗体的构造函数和OnCreate事件
窗体的构造函数和创建事件和OldCreateOrder属性有很大的关系. 情况1: 如果窗体继承自TForm,且有如下形式: 1. constructor TForm1.Create(AOw ...
- IOS AES加密之ECB128模式
1.AES加密模式有好几种,网上大多是CBC.256模式,找了好久才找到解决ECB128模式加密. AES需要导入头文件 #import <CommonCrypto/CommonCryptor. ...