【问题标题】:Nested fragments disappear during transition animation过渡动画期间嵌套片段消失
【发布时间】:2013-05-16 00:10:33
【问题描述】:

场景如下:Activity 包含片段A,而后者又使用getChildFragmentManager() 在其onCreate 中添加片段A1A2,如下所示:

getChildFragmentManager()
  .beginTransaction()
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

到目前为止,一切都很好,一切都按预期运行。

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

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .replace(R.id.fragmentHolder, new FragmentB())
  .addToBackStack(null)
  .commit()

在过渡期间,片段 Benter 动画运行正常,但片段 A1 和 A2 完全消失。当我们使用返回按钮恢复事务时,它们会正确初始化并在popEnter 动画期间正常显示。

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

getChildFragmentManager()
  .beginTransaction()
  .setCustomAnimations(enter, exit)
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

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

有什么方法可以实现吗?

编辑:请找一个测试用例here

Edit2:感谢@StevenByle 推动我继续尝试静态动画。显然你可以在每个操作的基础上设置动画(不是全局的整个事务),这意味着孩子可以有一个无限的静态动画集,而他们的父母可以有不同的动画,整个事情可以在一个事务中提交.请参阅下面的讨论和updated test case project

【问题讨论】:

  • 对于 A、A1、A2 等,R.id.fragmentHolder 是什么?
  • fragmentHolder 是 Activity 布局中的 id,fragment{One,Two}Holder 在 Fragment A 的布局中。这三个都是不同的。 Fragment A 最初是在 fragmentHolder 中添加的(即 Fragment B 正在替换 Fragment A)。
  • 我在这里创建了一个示例项目:github.com/BurntBrunch/NestedFragmentsAnimationsTest,存储库中还包含一个 apk。这是一个非常烦人的错误,我正在寻找一种解决方法(假设它不在我的代码中)。
  • 我现在对这个问题有了更多的了解。片段消失的原因是子级在父级之前处理生命周期事件。本质上,A1 和 A2 在 A 之前被移除,并且由于它们没有设置动画,它们会突然消失。一种缓解这种情况的方法是在替换 A 的事务中显式删除 A1 和 A2。这样,它们在退出时会进行动画处理,但是它们的动画速度是平方的,因为父容器也在进行动画处理。不产生这种人工制品的解决方案将不胜感激。
  • 您在问题中提到的更改(替换起始片段)是您真正想要做的还是只是一个例子?你只会调用一次changeFragment 方法吗?

标签: android android-fragments android-support-library android-nested-fragment


【解决方案1】:

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

【讨论】:

  • 我已将赏金授予 Luksprog,因为他的解决方案普遍适用。我确实尝试了静态动画技巧(持续时间实际上无关紧要 - 一旦父级消失,视图显然会消失)但它们在所有可能的情况下都不起作用(请参阅问题下的我的 cmets)。此外,这种方法泄漏了抽象,因为带有孩子的片段的父母需要知道这一事实并采取额外的步骤来设置孩子的动画。无论如何,非常感谢您的宝贵时间!
  • 同意,这与其说是防水解决方案,不如说是一种变通方法,并且可以被认为有点脆弱。但它适用于简单的情况。
【解决方案2】:

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

我制作了一个little example 并设置了背景图片(一些基本的东西)。

【讨论】:

  • 我决定奖励你,因为那是我最终使用的解决方案。非常感谢您的宝贵时间!
  • 哇,好肮脏。我们 Android 开发人员为了一些巧妙而不得不付出的努力
  • 我从第二个选项卡有一个 Viewpager 我正在替换其他片段,当我按下它时我需要显示 viewpager 第二个选项卡,它正在打开但它显示空白页。我尝试了您在上述线程中的建议,但仍然是一样的。
  • @Harish 写的用户回来时问题仍然存在
  • 哇,认真的吗?它的 2018 年,这仍然是一件事吗? :(
【解决方案3】:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

编辑: 我最终没有实现这个解决方案,因为它还有其他问题。 Square 最近推出了 2 个替换片段的库。我想说这实际上可能是一个比试图破解片段做谷歌不希望他们做的事情更好的选择。

http://corner.squareup.com/2014/01/mortar-and-flow.html

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

我想我会提出这个解决方案来帮助将来遇到这个问题的人。如果您追溯原始发布者与其他人的对话,并查看他发布的代码,您会看到原始发布者最终得出在子片段上使用无操作动画同时为父片段设置动画的结论。此解决方案并不理想,因为它会强制您跟踪所有子片段,这在将 ViewPager 与 FragmentPagerAdapter 一起使用时会很麻烦。

因为我到处都使用子片段,所以我想出了这个高效且模块化的解决方案(因此它可以很容易地删除),以防他们修复它并且这个无操作动画不再需要。

有很多方法可以实现这一点。我选择使用单例,我称之为 ChildFragmentAnimationManager。它基本上会根据其父片段为我跟踪一个子片段,并在被询问时对子片段应用无操作动画。

public class ChildFragmentAnimationManager {

private static ChildFragmentAnimationManager instance = null;

private Map<Fragment, List<Fragment>> fragmentMap;

private ChildFragmentAnimationManager() {
    fragmentMap = new HashMap<Fragment, List<Fragment>>();
}

public static ChildFragmentAnimationManager instance() {
    if (instance == null) {
        instance = new ChildFragmentAnimationManager();
    }
    return instance;
}

public FragmentTransaction animate(FragmentTransaction ft, Fragment parent) {
    List<Fragment> children = getChildren(parent);

    ft.setCustomAnimations(R.anim.no_anim, R.anim.no_anim, R.anim.no_anim, R.anim.no_anim);
    for (Fragment child : children) {
        ft.remove(child);
    }

    return ft;
}

public void putChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.add(child);
}

public void removeChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.remove(child);
}

private List<Fragment> getChildren(Fragment parent) {
    List<Fragment> children;

    if ( fragmentMap.containsKey(parent) ) {
        children = fragmentMap.get(parent);
    } else {
        children = new ArrayList<Fragment>(3);
        fragmentMap.put(parent, children);
    }

    return children;
}

}

接下来,您需要有一个扩展 Fragment 的类,您的所有 Fragment 都扩展(至少是您的 Child Fragment)。我已经上过这门课,我称之为 BaseFragment。创建片段视图时,我们将其添加到 ChildFragmentAnimationManager,并在其销毁时将其删除。您可以在附加/分离或序列中的其他匹配方法上执行此操作。我选择 Create/Destroy View 的逻辑是因为如果 Fragment 没有 View,我不关心动画它以继续被看到。这种方法也应该更适用于使用 Fragment 的 ViewPager,因为您不会跟踪 FragmentPagerAdapter 持有的每个 Fragment,而只会跟踪 3 个。

public abstract class BaseFragment extends Fragment {

@Override
public  View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().putChild(parent, this);
    }

    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onDestroyView() {
    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().removeChild(parent, this);
    }

    super.onDestroyView();
}

}

现在你所有的 Fragment 都由父 Fragment 存储在内存中,你可以像这样对它们调用 animate,你的子 Fragment 不会消失。

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ChildFragmentAnimationManager.instance().animate(ft, ReaderFragment.this)
                    .setCustomAnimations(R.anim.up_in, R.anim.up_out, R.anim.down_in, R.anim.down_out)
                    .replace(R.id.container, f)
                    .addToBackStack(null)
                    .commit();

另外,只要你有它,这里是你的 res/anim 文件夹中的 no_anim.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/linear_interpolator">
    <translate android:fromXDelta="0" android:toXDelta="0"
        android:duration="1000" />
</set>

再说一次,我不认为这个解决方案是完美的,但它比每个实例都有一个子片段要好得多,在父片段中实现自定义代码来跟踪每个子片段。我去过那里,不好玩。

【讨论】:

    【解决方案4】:

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

    getChildFragmentManager().beginTransaction()
            .setCustomAnimations(R.anim.none, R.anim.none, R.anim.none, R.anim.none)
            .add(R.id.container, nestedFragment)
            .commit();
    

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

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

    【讨论】:

    • 我做了一些非常相似的事情,但在更新孩子时使用了“显示”而不是“添加”。我还添加了“getChildFragmentManager().executePendingTransactions()”,尽管我不确定这是否是绝对必要的。不过,此解决方案工作得很好,并且不需要像某些人建议的那样“提供图像”片段。
    • 这很棒。但是,我在切换我的子片段时遇到了延迟。为避免这种情况,只需设置没有动画的第二个参数:fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
    【解决方案5】:

    我认为我找到了比 Luksprog 建议的将当前片段快照到位图更好的解决方案。

    诀窍是隐藏被移除或分离的片段,只有在动画完成后,片段才会在其自己的片段事务中被移除或分离。

    假设我们有FragmentAFragmentB,它们都有子片段。现在你通常会这样做:

    getSupportFragmentManager()
      .beginTransaction()
      .setCustomAnimations(anim1, anim2, anim1, anim2)
      .add(R.id.fragmentHolder, new FragmentB())
      .remove(fragmentA)    <-------------------------------------------
      .addToBackStack(null)
      .commit()
    

    你会这样做

    getSupportFragmentManager()
      .beginTransaction()
      .setCustomAnimations(anim1, anim2, anim1, anim2)
      .add(R.id.fragmentHolder, new FragmentB())
      .hide(fragmentA)    <---------------------------------------------
      .addToBackStack(null)
      .commit()
    
    fragmentA.removeMe = true;
    

    现在为 Fragment 的实现:

    public class BaseFragment extends Fragment {
    
        protected Boolean detachMe = false;
        protected Boolean removeMe = false;
    
        @Override
        public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
            if (nextAnim == 0) {
                if (!enter) {
                    onExit();
                }
    
                return null;
            }
    
            Animation animation = AnimationUtils.loadAnimation(getActivity(), nextAnim);
            assert animation != null;
    
            if (!enter) {
                animation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                    }
    
                    @Override
                    public void onAnimationEnd(Animation animation) {
                        onExit();
                    }
    
                    @Override
                    public void onAnimationRepeat(Animation animation) {
                    }
                });
            }
    
            return animation;
        }
    
        private void onExit() {
            if (!detachMe && !removeMe) {
                return;
            }
    
            FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
            if (detachMe) {
                fragmentTransaction.detach(this);
                detachMe = false;
            } else if (removeMe) {
                fragmentTransaction.remove(this);
                removeMe = false;
            }
            fragmentTransaction.commit();
        }
    }
    

    【讨论】:

    • popBackStack 会不会因为试图显示已分离的片段而导致错误?
    【解决方案6】:

    我能够想出一个非常干净的解决方案。 IMO 它是最简单的,虽然这在技术上是“绘制位图”解决方案,但至少它由片段库抽象。

    确保您的子片段使用以下内容覆盖父类:

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

    如果我们在子片段上有退出动画,它们将被动画化而不是眨眼。我们可以通过一个动画来利用这一点,该动画只是在一段时间内以完全 alpha 绘制子片段。这样,它们将在父片段动画时保持可见,从而提供所需的行为。

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

    【讨论】:

    • 它有帮助,谢谢。持续时间的价值无关紧要
    • 迄今为止最干净的解决方案
    【解决方案7】:

    所以似乎有很多不同的解决方法,但根据@Jayd16 的回答,我想我找到了一个非常可靠的包罗万象的解决方案,它仍然允许在子片段上自定义过渡动画,并且没有不需要对布局进行位图缓存。

    拥有一个扩展FragmentBaseFragment 类,并使您的所有片段都扩展该类(不仅仅是子片段)。

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

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

    此解决方案无需“猜测”父动画的持续时间(因此“什么都不做”动画将与父动画的退出动画具有相同的持续时间),并允许您仍然对子片段进行自定义动画(例如如果你用不同的动画交换子片段)。

    【讨论】:

    • 这是线程中我最喜欢的解决方案。不需要位图,不需要子片段中的任何额外代码,并且不会真正将信息从父片段泄漏到子片段。
    • @EugenPechanec 您需要来自 parent 片段的 nextAnim - 而不是来自子片段的。这就是重点。
    • 不幸的是,这种方法会导致子片段的内存泄漏,以及在 Lollipop 以下的 android 版本上隐式地导致父片段的内存泄漏:(
    • 感谢您提供这个非常有用的解决方案,但是它需要一些更新才能使用当前的支持库(27.0.2,不知道哪个版本破坏了此代码)。 mNextAnim现在位于 mAnimationInfoobject 中。你可以这样访问它:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo");animInfoField.setAccessible(true);Object animationInfo = animInfoField.get(fragment);Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
    • @DavidLericolais,希望在您的代码之后进一步添加另一行代码。 val nextAnimResource = nextAnimField.getInt(animationInfo); 替换行 int nextAnimResource = nextAnimField.getInt(fragment);
    【解决方案8】:

    我在使用地图片段时遇到了同样的问题。它在其包含的片段的退出动画期间不断消失。解决方法是为子地图片段添加动画,使其在父片段的退出动画期间保持可见。子片段的动画在其持续期间将其 alpha 保持在 100%。

    动画:res/animator/keep_child_fragment.xml

    <?xml version="1.0" encoding="utf-8"?>    
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <objectAnimator
            android:propertyName="alpha"
            android:valueFrom="1.0"
            android:valueTo="1.0"
            android:duration="@integer/keep_child_fragment_animation_duration" />
    </set>
    

    然后在将地图片段添加到父片段时应用动画。

    父片段

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    
        View view = inflater.inflate(R.layout.map_parent_fragment, container, false);
    
        MapFragment mapFragment =  MapFragment.newInstance();
    
        getChildFragmentManager().beginTransaction()
                .setCustomAnimations(R.animator.keep_child_fragment, 0, 0, 0)
                .add(R.id.map, mapFragment)
                .commit();
    
        return view;
    }
    

    最后,在资源文件中设置子片段动画的持续时间。

    values/integers.xml

    <resources>
      <integer name="keep_child_fragment_animation_duration">500</integer>
    </resources>
    

    【讨论】:

      【解决方案9】:

      为了动画嵌套片段的消失,我们可以在 ChildFragmentManager 上强制弹出堆栈。这将触发过渡动画。为此,我们需要赶上 OnBackButtonPressed 事件或监听 backstack 的变化。

      这里是代码示例。

      View.OnClickListener() {//this is from custom button but you can listen for back button pressed
                  @Override
                  public void onClick(View v) {
                      getChildFragmentManager().popBackStack();
                      //and here we can manage other fragment operations 
                  }
              });
      
        Fragment fr = MyNeastedFragment.newInstance(product);
      
        getChildFragmentManager()
                .beginTransaction()
                      .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
                      .replace(R.neasted_fragment_container, fr)
                      .addToBackStack("Neasted Fragment")
                      .commit();
      

      【讨论】:

        【解决方案10】:

        我最近在我的问题中遇到了这个问题:Nested fragments transitioning incorrectly

        我有一个解决方案,可以在不保存位图、也不使用反射或任何其他不满意的方法的情况下解决此问题。

        可以在此处查看示例项目:https://github.com/zafrani/NestedFragmentTransitions

        可以在此处查看效果的 GIF:https://imgur.com/94AvrW4

        在我的示例中,有 6 个子片段,被分成两个父片段。我能够毫无问题地实现进入、退出、弹出和推送的转换。配置更改和后按也成功处理。

        大部分解决方案在我的 BaseFragment(由我的子片段和父片段扩展的片段)的 onCreateAnimator 函数中,如下所示:

           override fun onCreateAnimator(transit: Int, enter: Boolean, nextAnim: Int): Animator {
            if (isConfigChange) {
                resetStates()
                return nothingAnim()
            }
        
            if (parentFragment is ParentFragment) {
                if ((parentFragment as BaseFragment).isPopping) {
                    return nothingAnim()
                }
            }
        
            if (parentFragment != null && parentFragment.isRemoving) {
                return nothingAnim()
            }
        
            if (enter) {
                if (isPopping) {
                    resetStates()
                    return pushAnim()
                }
                if (isSuppressing) {
                    resetStates()
                    return nothingAnim()
                }
                return enterAnim()
            }
        
            if (isPopping) {
                resetStates()
                return popAnim()
            }
        
            if (isSuppressing) {
                resetStates()
                return nothingAnim()
            }
        
            return exitAnim()
        }
        

        活动和父片段负责设置这些布尔值的状态。从我的示例项目中查看方式和位置更容易。

        在我的示例中我没有使用支持片段,但可以对它们及其 onCreateAnimation 函数使用相同的逻辑

        【讨论】:

          【解决方案11】:

          您可以在子片段中执行此操作。

          @Override
          public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
              if (true) {//condition
                  ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(getView(), "alpha", 1, 1);
                  objectAnimator.setDuration(333);//time same with parent fragment's animation
                  return objectAnimator;
              }
              return super.onCreateAnimator(transit, enter, nextAnim);
          }
          

          【讨论】:

          • 谢谢!也许不是最好的,但可能是最简单的解决方案。
          【解决方案12】:

          解决这个问题的一个简单方法是使用这个库中的Fragment 类而不是标准库片段类:

          https://github.com/marksalpeter/contract-fragment

          附带说明一下,该包还包含一个有用的委托模式,称为 ContractFragment,您可能会发现它对利用父子片段关系构建应用很有用。

          【讨论】:

            【解决方案13】:

            根据@kcoppock的上述回答,

            如果您有 Activity->Fragment->Fragments(多个堆叠,以下有帮助),请对最佳答案恕我直言进行小幅编辑。

            public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
            
                final Fragment parent = getParentFragment();
            
                Fragment parentOfParent = null;
            
                if( parent!=null ) {
                    parentOfParent = parent.getParentFragment();
                }
            
                if( !enter && parent != null && parentOfParent!=null && parentOfParent.isRemoving()){
                    Animation doNothingAnim = new AlphaAnimation(1, 1);
                    doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
                    return doNothingAnim;
                } else
                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);
                }
            }
            

            【讨论】:

              【解决方案14】:

              我的问题是删除父片段 (ft.remove(fragment)),子动画没有发生。

              基本问题是子片段在父片段退出动画之前立即被销毁。

              子片段自定义动画不会在父片段移除时执行

              正如其他人所逃避的那样,在移除 PARENT 之前隐藏 PARENT(而不是孩子)是可行的方法。

                          val ft = fragmentManager?.beginTransaction()
                          ft?.setCustomAnimations(R.anim.enter_from_right,
                                  R.anim.exit_to_right)
                          if (parentFragment.isHidden()) {
                              ft?.show(vehicleModule)
                          } else {
                              ft?.hide(vehicleModule)
                          }
                          ft?.commit()
              

              如果您真的想删除父级,您可能应该在自定义动画上设置一个侦听器以了解动画何时结束,这样您就可以安全地对父级片段进行一些最终确定(删除)。如果您不及时执行此操作,您最终可能会终止动画。 N.B 动画是在它自己的异步队列上完成的。

              顺便说一句,您不需要子片段上的自定义动画,因为它们将继承父动画。

              【讨论】:

                【解决方案15】:

                该问题已在 androidx.fragment:fragment:1.2.0-alpha02 中修复。详情请见https://issuetracker.google.com/issues/116675313

                【讨论】:

                  【解决方案16】:

                  旧线程,但以防有人在这里绊倒:

                  以上所有方法对我来说都没有吸引力,位图解决方案非常肮脏且性能不佳;其他的要求子片段了解用于创建相关子片段的事务中使用的转换的持续时间。在我看来,更好的解决方案如下:

                  val currentFragment = supportFragmentManager.findFragmentByTag(TAG)
                  val transaction = supportFragmentManager
                      .beginTransaction()
                      .setCustomAnimations(anim1, anim2, anim1, anim2)
                      .add(R.id.fragmentHolder, FragmentB(), TAG)
                  if (currentFragment != null) {
                      transaction.hide(currentFragment).commit()
                      Handler().postDelayed({
                          supportFragmentManager.beginTransaction().remove(currentFragment).commit()
                      }, DURATION_OF_ANIM)
                  } else {
                      transaction.commit()
                  }
                  

                  我们只是隐藏当前片段并添加新片段,当动画完成后我们删除旧片段。这样,它在一个地方处理,并且不创建位图。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2011-06-23
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-04-02
                    • 1970-01-01
                    • 2016-11-26
                    • 2021-01-12
                    相关资源
                    最近更新 更多