【问题标题】:setRetainInstance(true) + setCustomAnimations(...) = animation for each orientation change?setRetainInstance(true) + setCustomAnimations(...) = 每个方向变化的动画?
【发布时间】:2015-03-03 12:56:29
【问题描述】:

背景

我有一个带有片段的活动,该片段在创建时需要动画,但在方向改变时不需要。

片段被动态插入到布局中,因为它是导航抽屉式活动的一部分。

问题

我想避免为配置更改重新创建片段,所以我在片段中使用了 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" />

我尝试过的

  • 我尝试通过使用“替换”而不是“添加”来修复它。它没有帮助。
  • 我还尝试始终执行片段的替换,如果片段已经存在,则在没有动画的情况下执行(在同一片段上)。
  • 如果我删除 setRetainInstance 调用,它可以工作,但我想避免重新创建片段。

问题

  1. 我该如何解决这个问题?
  2. 为什么我仍然会看到添加片段的动画?
  3. 当其他配置发生变化时会发生什么?

解决方法 #1

此解决方案通常有效,但会对您尝试实现的生命周期造成不良影响:

    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)。

解决方法 #2

解决此问题的一种方法是避免通过片段管理器添加动画,而只在片段生命周期内为视图本身执行此操作。 看起来是这样的:

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

【问题讨论】:

  • if I remove the setRetainInstance call, it works,那它是如何工作的呢?你的意思是你需要在每次方向变化时重新创建和重新添加片段(有或没有动画)?
  • @user3249477 是的。片段将被重新创建。
  • 每次方向改变时,您的活动是否都会被销毁并重新创建?
  • 根据@ItaiHanski,请发布您的清单文件
  • 你们,清单中没有什么特别之处。它只是一个简单的活动,其中包含一个片段,以我编写的相同方式创建。您更改了方向,并且正在重新创建活动,而片段仍应用于它。

标签: android animation android-fragments


【解决方案1】:

如果片段在旋转后重新创建,您如何覆盖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);
}

这个片段应该是这个活动中所有片段都将扩展的基础片段。

【讨论】:

  • 稍作修改后,这似乎可行(尽管它仍然是一种解决方法,因为它需要对片段而不是活动进行更改)。我可以编辑您的答案以放置相关代码吗?
  • 当然,去吧。很高兴它有帮助!
  • 好的,如果你能找到更官方的解决方案,请告诉我。
  • 我会的,我也需要这个:)
  • 谢谢。虽然如果你有一个片段消失时它会阻止动画。例如,当用户按下按钮关闭此片段时。我还检查了 'enter' 参数,并且仅在为真时才阻止动画以避免这种情况。
【解决方案2】:

再看看你的代码:

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 添加到事务中。

这就是为什么您每次都会重新创建动画和片段的原因。只需对findadd 使用相同的标签就可以了。

【讨论】:

  • 这其实是同一个片段。我在这里重命名了它,但似乎我忘记在任何地方重命名它。现在将更新代码。这不是问题的原因。简而言之,您发现的是我在编写问题时遇到的一个错字,而不是在实际代码中。
【解决方案3】:

为什么会发生这种情况,因为当片段管理器尝试在方向上重新附加片段时,它会应用已存储在事务中的动画。 (即您在添加片段时设置的)。

我们通过处理活动本身的方向变化来解决这个问题。如果要更改 UI 设计,请在 onConfigChanges 方法中附加和分离片段。

【讨论】:

  • 所以你说这是故意的?这是否意味着您是 Google 的开发人员之一?如果是这样,您能否添加覆盖此行为的功能?
【解决方案4】:

我研究了代码,貌似动画信息是保存在Fragment.animationInfo中的,这个值是由

设置的
BackStackState.executeOps() -> Fragment.setNextAnim(int animResourceId)

默认情况下,重新创建fragment会丢弃动画信息,但是如果设置fragment 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-13
    • 2011-06-18
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 2014-07-07
    相关资源
    最近更新 更多