简体   繁体   English

setRetainInstance(true)+ setCustomAnimations(...)=每个方向更改的动画?

[英]setRetainInstance(true) + setCustomAnimations(…) = animation for each orientation change?

Background 背景

I have an activity with a fragment that needs to be animated when being created, but not when the orientation changes . 我有一个活动,片段需要在创建时动画,但不是在方向改变时。

The fragment is being inserted into a layout dynamically, as it's a part of a navigation-drawer-style activity. 片段被动态插入到布局中,因为它是导航抽屉式活动的一部分。

The problem 问题

I wanted to avoid re-creating the fragment for configuration changes, so I used setRetainInstance in the fragment. 我想避免为配置更改重新创建片段,所以我在片段中使用了setRetainInstance。 It works, but for some reason the animation also restarts each time I rotate the device. 它可以工作,但由于某些原因,每次旋转设备时动画也会重新启动。

What I've done 我做了什么

I've added this to the fragment: 我已将此添加到片段中:

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
}

and this to the activity: 这对活动:

    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 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" />

What I've tried 我试过的

  • I've tried to fix it by using "replace" instead of "add". 我试图通过使用“替换”而不是“添加”来修复它。 It didn't help. 它没有帮助。
  • I've also tried to always perform the replacement of the fragment, and if the fragment is already there, do it without animation (on the same fragment). 我也试图总是执行片段的替换,如果片段已经存在,那么就不用动画(在同一片段上)。
  • if I remove the setRetainInstance call, it works, but I want to avoid re-creating the fragment. 如果我删除setRetainInstance调用,它可以工作,但我想避免重新创建片段。

Question

  1. How can I solve this issue? 我该如何解决这个问题?
  2. Why do I still get an animation for the adding of the fragment? 为什么我仍然可以获得添加片段的动画?
  3. What happens when other configurations change? 当其他配置改变时会发生什么?

Workaround #1 解决方法#1

This solution works in general, but it causes bad things to the lifecycle you've tried to achieve : 这个解决方案通常起作用,但它会给你试图实现的生命周期带来不好的结果:

    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();
            }
        });
    }

I would still want to see what can be done, because this can cause things you didn't want to occur(onDetach for the fragment, for example). 我仍然希望看到可以做什么,因为这可能导致你不想发生的事情(例如onDetach片段)。

Workaround #2 解决方法#2

One way to solve this is to avoid adding the animation via the fragmentManager, and just do it for the view itself within the fragment lifecycle. 解决此问题的一种方法是避免通过fragmentManager添加动画,并在片段生命周期内为视图本身执行此操作。 This is how it looks like: 这是它的样子:

BaseFragment 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));
}

How about you override the onCreateAnimation() method and prevent the animation from happening if the fragment is being recreated after a rotation? 你如何覆盖onCreateAnimation()方法并防止在旋转后重新创建片段时发生动画?

As shown here: How to disable/avoid Fragment custom animations after screen rotation 如下所示: 如何在屏幕旋转后禁用/避免片段自定义动画


EDIT: Here's a sample code: 编辑:这是一个示例代码:

BaseFragment.java 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);
}

This fragment should be the base one that all fragments in this activity will extend. 此片段应该是此活动中所有片段将扩展的基础片段。

take a look at your code again: 再看看你的代码:

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();
}

you're using MyFragment.TAG to find the fragment, but you're adding to transaction with PhoneInsertionFragment.TAG . 您正在使用MyFragment.TAG找到片段,但你增加交易与PhoneInsertionFragment.TAG

That's why you're getting the animation and the fragment re-creating every time. 这就是为什么你每次都要重新创建动画和片段的原因。 Just use the same tag for find and add and it should be fine. 只需使用相同的标签进行findadd

Why it is occurring because when fragment manager tries to re-attach the fragment on orientation, it applies the animations which already stored in the transaction. 为什么会发生这种情况,因为当片段管理器尝试在方向上重新附加片段时,它会应用已存储在事务中的动画。 (ie which you set when the fragment was added). (即添加片段时设置的)。

We solved this by handling orientation change in activity itself. 我们通过处理活动本身的方向变化来解决这个问题 If you want to change the UI design, attach and detach the fragment in onConfigChanges method. 如果要更改UI设计,请在onConfigChanges方法中附加和分离片段。

I studied the code, looks like the animation information is saved in Fragment.animationInfo, this value is set by 我研究了代码,看起来动画信息保存在Fragment.animationInfo中,这个值是由设置的

BackStackState.executeOps() -> Fragment.setNextAnim(int animResourceId)

By default, re-creating the fragment will discard the animation information, but if you set fragment retainInstance, the animation information will also is retained 默认情况下,重新创建片段将丢弃动画信息,但如果设置片段retainInstance,则动画信息也将保留

My solution: 我的解决方案

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
    }
}

I only tested support.v4.app.Fragment, but I think this solution also applies to android.app.Fragment 我只测试了support.v4.app.Fragment,但我认为这个解决方案也适用于android.app.Fragment

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM