过渡动画期间嵌套片段消失

Del*_*yan 99 android android-fragments android-support-library android-nested-fragment

这里的情景:活动包含片段A,又使用getChildFragmentManager()添加片段A1,并A2在其onCreate像这样:

getChildFragmentManager()
  .beginTransaction()
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()
Run Code Online (Sandbox Code Playgroud)

到目前为止,这么好,一切都按预期运行.

然后,我们在Activity中运行以下事务:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .replace(R.id.fragmentHolder, new FragmentB())
  .addToBackStack(null)
  .commit()
Run Code Online (Sandbox Code Playgroud)

在转换期间,enter片段的动画B正确运行,但片段A1和A2完全消失.当我们使用"后退"按钮恢复事务时,它们会正确初始化并在popEnter动画期间正常显示.

在我的简短测试中,它更奇怪 - 如果我为子片段设置动画(见下文),exit当我们添加片段时,动画会间歇性地运行B

getChildFragmentManager()
  .beginTransaction()
  .setCustomAnimations(enter, exit)
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()
Run Code Online (Sandbox Code Playgroud)

我想要实现的效果很简单 - 我希望片段(anim2)上的exit(或应该是popExit?)动画A运行,为整个容器(包括其嵌套的子容器)设置动画.

有没有办法实现这一目标?

编辑:请在这里找到一个测试用例

编辑2:感谢@StevenByle推动我继续尝试使用静态动画.显然你可以在每个操作的基础上设置动画(不是整个事务的全局),这意味着孩子们可以有一个不确定的静态动画集,而他们的父可以有不同的动画,整个事情可以在一个事务中提交.请参阅下面的讨论和更新的测试用例项目.

kco*_*ock 68

所以似乎有很多不同的解决方法,但基于@ Jayd16的答案,我想我已经找到了一个非常可靠的全能解决方案仍然允许对子片段进行自定义过渡动画,并且不需要做布局的位图缓存.

有一个BaseFragment扩展的类,Fragment并使所有的片段扩展到该类(不仅仅是子片段).

在该BaseFragment课程中,添加以下内容:

// Arbitrary value; set it to some reasonable default
private static final int DEFAULT_CHILD_ANIMATION_DURATION = 250;

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    final Fragment parent = getParentFragment();

    // Apply the workaround only if this is a child fragment, and the parent
    // is being removed.
    if (!enter && parent != null && parent.isRemoving()) {
        // This is a workaround for the bug where child fragments disappear when
        // the parent is removed (as all children are first removed from the parent)
        // See https://code.google.com/p/android/issues/detail?id=55228
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }
}

private static long getNextAnimationDuration(Fragment fragment, long defValue) {
    try {
        // Attempt to get the resource ID of the next animation that
        // will be applied to the given fragment.
        Field nextAnimField = Fragment.class.getDeclaredField("mNextAnim");
        nextAnimField.setAccessible(true);
        int nextAnimResource = nextAnimField.getInt(fragment);
        Animation nextAnim = AnimationUtils.loadAnimation(fragment.getActivity(), nextAnimResource);

        // ...and if it can be loaded, return that animation's duration
        return (nextAnim == null) ? defValue : nextAnim.getDuration();
    } catch (NoSuchFieldException|IllegalAccessException|Resources.NotFoundException ex) {
        Log.w(TAG, "Unable to load next animation from parent.", ex);
        return defValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,它需要反思; 但是,由于此解决方法适用于支持库,因此除非更新支持库,否则不会冒更改底层实现的风险.如果要从源构建支持库,则可以为下一个动画资源ID添加一个访问器,Fragment.java并消除对反射的需要.

此解决方案无需"猜测"父级的动画持续时间(因此"不执行任何操作"动画将具有与父级退出动画相同的持续时间),并允许您仍然对子级片段执行自定义动画(例如,如果您'用不同的动画重新交换儿童片段).

  • 感谢您提供这个非常有用的解决方案,但是需要稍微更新才能使用当前的支持库(27.0.2,不知道哪个版本破坏了此代码).`mNextAnim`现在位于`mAnimationInfo`object内.您可以像这样访问它:`Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo");``animInfoField.setAccessible(true);``Object animationInfo = animInfoField.get(fragment);``Field nextAnimField = animationInfo.的getClass()getDeclaredField( "mNextAnim");` (7认同)
  • 这是我在线程中最喜欢的解决方案.不需要位图,不需要子片段中的任何额外代码,也不会将父信息从父片段泄漏到子片段. (5认同)
  • @DavidLericolais,想进一步添加另一行代码.`val nextAnimResource = nextAnimField.getInt(animationInfo);`替换行`int nextAnimResource = nextAnimField.getInt(fragment);` (4认同)

Luk*_*rog 36

为了避免用户在事务中删除/替换父片段时看到嵌套片段消失,您可以通过提供它们的图像来"模拟"那些仍然存在的片段,就像它们出现在屏幕上一样.此图像将用作嵌套片段容器的背景,因此即使嵌套片段的视图消失,图像也会模拟它们的存在.此外,我没有看到与嵌套片段的视图的交互性失去作为一个问题,因为我认为你不希望用户在他们刚刚被删除的过程中对他们采取行动(可能作为用户操作好).

我在设置背景图片(基本的东西)方面做了一个小例子.

  • 哇,好肮脏.我们Android开发人员只需要花一些时间 (15认同)

Jay*_*d16 31

我能够提出一个非常干净的解决方案.IMO是最不起眼的,虽然这在技术上是"绘制位图"解决方案,至少它是由片段lib抽象的.

确保您的子frags使用以下方法覆盖父类:

private static final Animation dummyAnimation = new AlphaAnimation(1,1);
static{
    dummyAnimation.setDuration(500);
}

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if(!enter && getParentFragment() != null){
        return dummyAnimation;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}
Run Code Online (Sandbox Code Playgroud)

如果我们在子frags上有一个退出动画,它们将被动画而不是闪烁.我们可以通过制作一个动画来简单地以完整的alpha绘制儿童片段一段时间来利用它.这样,它们将在父片段中保持可见,因为它会动画,从而产生所需的行为.

我能想到的唯一问题是跟踪持续时间.我可以将它设置为一个大数字,但我担心如果它仍然在某处绘制动画可能会出现性能问题.


Mau*_*ycy 15

为了清楚起见,我发布了我的解 解决方案非常简单.如果您正在尝试模仿父级的片段事务动画,只需将自定义动画添加到具有相同持续时间的子片段事务.哦,并确保在add()之前设置自定义动画.

getChildFragmentManager().beginTransaction()
        .setCustomAnimations(R.anim.none, R.anim.none, R.anim.none, R.anim.none)
        .add(R.id.container, nestedFragment)
        .commit();
Run Code Online (Sandbox Code Playgroud)

R.anim.none的xml(我的父母进入/退出动画时间是250ms)

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="0" android:duration="250" />
</set>
Run Code Online (Sandbox Code Playgroud)


Ste*_*yle 7

我知道这可能无法完全解决您的问题,但也许它会满足其他人的需求,您可以为您的孩子添加enter/ exitpopEnter/ popExit动画Fragment,这些动画实际上不会移动/动画Fragment.只要动画具有与其父Fragment动画相同的持续时间/偏移量,它们就会显示为与父动画一起移动/动画.