我有一个活动,其片段需要在创建时进行动画处理,但在方向更改时则不需要。
片段正在动态插入布局,因为它是导航抽屉式活动的一部分。
我想避免为配置更改重新创建片段,所以我在片段中使用了setRetainInstance。 它可以工作,但由于某些原因,每次旋转设备时动画也会重新启动。
我已将此添加到片段中:
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
这是活动:
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MyFragment fragment= (MyFragment) fragmentManager.findFragmentByTag(MyFragment.TAG);
if (fragment== null) {
fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right);
fragment= new MyFragment();
fragmentTransaction
.add(R.id.fragmentContainer, fragment, MyFragment.TAG).commit();
}
fragment_container.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
此解决方案通常有效,但它会给您尝试实现的生命周期带来不利影响:
MyFragment fragment= (MyFragment) fragmentManager.findFragmentByTag(MyFragment.TAG);
if (MyFragment== null) {
MyFragment= new MyFragment();
fragmentManager.beginTransaction().setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right)
.replace(R.id.fragmentContainer, fragment, MyFragment.TAG).commit();
} else {
//workaround: fragment already exists, so avoid re-animating it by quickly removing and re-adding it:
fragmentManager.beginTransaction().remove(fragment).commit();
final Fragment finalFragment = fragment;
new Handler().post(new Runnable() {
@Override
public void run() {
fragmentManager.beginTransaction().replace(R.id.fragmentContainer, fragment, finalFragment .TAG).commit();
}
});
}
我仍然希望看到可以做什么,因为这会导致你不想发生的事情(例如onDetach for the fragment)。
解决此问题的一种方法是避免通过fragmentManager添加动画,并在片段生命周期内为视图本身执行此操作。 这就是它的样子:
BaseFragment
@Override
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
if (savedInstanceState == null)
rootView.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_in_from_left));
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (!getActivity().isChangingConfigurations())
getView().startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.fade_out));
}
答案 0 :(得分:9)
如果在轮换后重新创建片段,则覆盖onCreateAnimation()
方法并阻止动画发生?
如下所示:How to disable/avoid Fragment custom animations after screen rotation
编辑:这是一个示例代码:
<强> BaseFragment.java 强>
...
private boolean mNeedToAvoidAnimation;
@Override
public void onDestroyView() {
super.onDestroyView();
mNeedToAvoidAnimation = true;
}
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
// This avoids the transaction animation when the orienatation changes
boolean needToAvoidAnimation = mNeedToAvoidAnimation;
mNeedToAvoidAnimation = false;
return needToAvoidAnimation ? new Animation() {
} : super.onCreateAnimation(transit, enter, nextAnim);
}
此片段应该是此活动中所有片段都将扩展的基础片段。
答案 1 :(得分:0)
再次查看您的代码:
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MyFragment fragment= (MyFragment)
fragmentManager
.findFragmentByTag(MyFragment.TAG);
if (fragment== null) {
fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right);
fragment= new MyFragment();
fragmentTransaction
.add(R.id.fragmentContainer, fragment,
PhoneInsertionFragment.TAG).commit();
}
您正在使用MyFragment.TAG
查找该片段,但是您正在使用PhoneInsertionFragment.TAG
添加到该事务中。
这就是为什么你每次都要重新制作动画和片段的原因。只需对find
和add
使用相同的标记,就可以了。
答案 2 :(得分:0)
为什么会发生这种情况,因为当片段管理器尝试在方向上重新附加片段时,它会应用已存储在事务中的动画。 (即你在添加片段时设置的)。
我们通过处理活动本身的方向变化来解决这个问题。如果要更改UI设计,请在onConfigChanges方法中附加和分离片段。
答案 3 :(得分:0)
我研究了代码,看起来动画信息保存在Fragment.animationInfo中,此值由
设置BackStackState.executeOps() -> Fragment.setNextAnim(int animResourceId)
默认情况下,重新创建片段将丢弃动画信息,但如果设置片段retainInstance,动画信息也将保留
我的解决方案:
class TestFragment : Fragment() {
override fun onDetach() {
super.onDetach()
if (retainInstance) {
// use reflect clear retain Animation
reflectField<Fragment>("mAnimationInfo").set(this, null)
}
}
@Throws(NoSuchFieldException::class, SecurityException::class)
inline fun <reified T : Any> reflectField(name: String): Field {
val field = T::class.java.getDeclaredField(name)
field.isAccessible = true
return field
}
}
我只测试了support.v4.app.Fragment,但我认为这个解决方案也适用于android.app.Fragment