当前位置: 首页 > 知识库问答 >
问题:

嵌套片段转换不正确

段干麒
2023-03-14

你好,堆栈溢出的好程序员!我已经花了一个星期的时间来解决这个问题,现在非常渴望一个解决方案。

我正在使用android。应用程序。不要将片段与支持片段混淆。

我有6个子片段名为:

  • <代码>片段一
  • 碎片二
  • 碎片三
  • 碎片a
  • 碎片b
  • FragmentC

我有2个名为的父片段:

  • 碎片数字
  • 碎片Alpha

我有1个活动名为:

  • 主活动

其表现如下:

  • 子片段是仅显示视图的片段,它们既不显示也不包含片段
  • 父片段用单个子片段填充其整个视图。他们可以用其他子片段替换子片段
  • 该活动用父片段填充其大部分视图。它可以用其他父片段替换它。因此,在任何时候,屏幕上都只显示一个子片段

你可能已经猜到了,

FragmentNumics显示子片段FragmentOneFragmentTwoFragmentThree

<代码>片段Alpha显示子片段<代码>片段A、<代码>片段B和<代码>片段C。

我正在尝试转换/设置父片段和子片段的动画。子片段平稳过渡,如预期。然而,当我转换到一个新的父片段时,它看起来很糟糕。子片段看起来像是从其父片段运行独立的转换。子片段看起来也从父片段中删除了。可以在此处查看它的gifhttps://imgur.com/kOAotvk.注意我单击“显示Alpha”时发生的情况。

最接近的问题

我有以下动画效果(出于测试目的,持续时间很长):

fragment\u输入。xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="4000"
        android:interpolator="@android:anim/linear_interpolator"
        android:propertyName="xFraction"
        android:valueFrom="1.0"
        android:valueTo="0" />
</set>

fragment\u退出。xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="4000"
        android:interpolator="@android:anim/linear_interpolator"
        android:propertyName="xFraction"
        android:valueFrom="0"
        android:valueTo="-1.0" />
</set>

fragment_pop.xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="4000"
        android:interpolator="@android:anim/linear_interpolator"
        android:propertyName="xFraction"
        android:valueFrom="0"
        android:valueTo="1.0" />
</set>

fragment_push.xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="4000"
        android:interpolator="@android:anim/linear_interpolator"
        android:propertyName="xFraction"
        android:valueFrom="-1.0"
        android:valueTo="0" />
</set>

fragment\u无任何内容。xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="4000" />
</set>

需要考虑的事项:第一个父片段FragmentNumeric没有输入效果,因此它总是与活动一起准备好,并且没有退出效果,因为没有任何东西正在退出。我还使用了FragmentTransaction添加,其中FragmentAlpha使用的是replace

class MainActivity : AppCompatActivity {

    fun showFragmentNumeric(){
        this.fragmentManager.beginTransaction()
                .setCustomAnimations(R.animator.fragment_nothing,
                                     R.animator.fragment_nothing,
                                     R.animator.fragment_push,
                                     R.animator.fragment_pop)
                .add(this.contentId, FragmentNumeric(), "FragmentNumeric")
                .addToBackStack("FragmentNumeric")
                .commit()
}

    fun showFragmentAlpha(){
        this.fragmentManager.beginTransaction()
                .setCustomAnimations(R.animator.fragment_enter,
                                     R.animator.fragment_exit,
                                     R.animator.fragment_push,
                                     R.animator.fragment_pop)
                .replace(this.contentId, FragmentAlpha(), "FragmentAlpha")
                .addToBackStack("FragmentAlpha")
                .commit()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState == null) {
            showFragmentNumeric()
        }
    }
}

在快速显示其第一个子片段方面,执行与活动相同的操作。

class FragmentNumeric : Fragment {

    fun showFragmentOne(){
            this.childFragmentManager.beginTransaction()
                    .setCustomAnimations(R.animator.fragment_nothing,
                                         R.animator.fragment_nothing,
                                         R.animator.fragment_push,
                                         R.animator.fragment_pop)
                    .add(this.contentId, FragmentOne(), "FragmentOne")
                    .addToBackStack("FragmentOne")
                    .commit()
    }

    fun showFragmentTwo(){
        this.childFragmentManager.beginTransaction()
                .setCustomAnimations(R.animator.fragment_enter,
                                     R.animator.fragment_exit,
                                     R.animator.fragment_push,
                                     R.animator.fragment_pop)
                .replace(this.contentId, FragmentTwo(), "FragmentTwo")
                .addToBackStack("FragmentTwo")
                .commit()
    }


    fun showFragmentThree(){
        this.childFragmentManager.beginTransaction()
                .setCustomAnimations(R.animator.fragment_enter,
                                     R.animator.fragment_exit,
                                     R.animator.fragment_push,
                                     R.animator.fragment_pop)
                .replace(this.contentId, FragmentThree(), "FragmentThree")
                .addToBackStack("FragmentThree")
                .commit()
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (savedInstanceState == null) {
            if (this.childFragmentManager.backStackEntryCount <= 1) {
                showFragmentOne()
            }
        }
    }
}

FragmentAlpha遵循与FragmentNumeric相同的模式,分别用片段A、B和C替换片段1、2和3。

子片段只是显示以下XML视图,并动态设置其文本和按钮单击侦听器,以从父片段或活动调用函数。

view_child_example.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background"
    android:clickable="true"
    android:focusable="true"
    android:orientation="vertical">

    <TextView
        android:id="@+id/view_child_example_header"
        style="@style/Header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


    <Button
        android:id="@+id/view_child_example_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"  />
</LinearLayout>

使用匕首和一些合约,我让子片段回调到它们的父片段并通过执行以下操作来托管活动:

FragmentOne设置单击侦听器按钮要执行的操作:

(parentFragment as FragmentNumeric).showFragmentTwo()

FragmentTwo设置按钮点击监听器要做:

(parentFragment as FragmentNumeric).showFragmentThree()

FragmentThree不同,它将设置click listener执行以下操作:

(activity as MainActivity).showFragmentAlpha()

有人能解决这个问题吗?

我已根据要求添加了一个示例项目:https://github.com/zafrani/NestedFragmentTransitions

它与原始视频中的不同之处在于,父片段不再使用具有xFraction属性的视图。所以看起来enter动画不再有那种重叠效果了。但是,它仍然会从父片段中移除子片段,并将它们并排设置动画。动画完成后,片段3立即替换为片段A。

父片段视图和子片段视图都使用xFraction属性。关键是在父对象设置动画时抑制子对象的动画。

共有2个答案

屠兴旺
2023-03-14

您正在获得如此有线的结果,因为您正在为片段使用退出动画。基本上,我们每次都会遇到片段动画的问题,最后,每次我们进行转换时,都会从源片段动画转移到使用自己的片段动画。

1)要验证此行为,您只需删除片段的退出动画,一切都会好起来的。在大多数情况下,这应该就足够了,因为退出动画非常具体,并且仅用于单个片段管理(在您的情况下,不包括儿童)

getFragmentManager().beginTransaction()
                .setCustomAnimations(R.animator.enter_anim_frag1,
                                     0,
                                     R.animator.enter_anim_frag2,
                                     0)
                .replace(xxx, Xxx1, Xxx2)
                .addToBackStack(null)
                .commit()

2) 另一个可能适合的选择是考虑应用程序结构,并尽量避免用动画替换片段。如果需要替换,请不要使用动画,并且可以添加任何动画(包括“输入”和“退出”),但只能使用“添加”。

谷梁云瀚
2023-03-14

我想我已经找到了一种使用片段#onCreateAnimator解决这个问题的方法。过渡的gif可以在这里查看:https://imgur.com/94AvrW4.

我为测试做了一个PR,到目前为止,它按照我的预期工作,并在配置更改和支持后退按钮后幸存下来。这里是链接https://github.com/zafrani/NestedFragmentTransitions/pull/1/files#diff-c120dd82b93c862b01c2548bdcafcb20R25

父片段和子片段的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()
}

布尔值设置在不同的场景中,在PR中更容易看到。

动画功能包括:

private fun enterAnim(): Animator { 
        return AnimatorInflater.loadAnimator(activity, R.animator.fragment_enter)
    }

    private fun exitAnim(): Animator { 
        return AnimatorInflater.loadAnimator(activity, R.animator.fragment_exit)
    }

    private fun pushAnim(): Animator { 
        return AnimatorInflater.loadAnimator(activity, R.animator.fragment_push)
    }

    private fun popAnim(): Animator { 
        return AnimatorInflater.loadAnimator(activity, R.animator.fragment_pop)
    }

    private fun nothingAnim(): Animator { 
        return AnimatorInflater.loadAnimator(activity, R.animator.fragment_nothing)
    }

如果有人找到更好的方法,我会把问题留着。

 类似资料:
  • 我正在开发一个包含一个活动和多个片段的android应用程序。我的应用程序包含导航抽屉。它的布局包含listview。单击它的项目,我用ft.replace(R.id.my\u placehodler,new MyFragment())动态更改片段,并将事务添加到。当我每次实例化新片段时都进行新事务。在我看来,这不是一个好办法。你能给我一些关于进行片段事务的正确方法的建议吗?

  • 我有4个片段,其中一个片段中有一个viewpager和一个不同的类,我用3个其他片段定义了viewpager的适配器,第一次打开此片段时,所有子片段都正确显示在viewpager中,但当我切换(使用transaction.replace)到另一个片段并再次返回时,子片段消失了,我不能使用ChildFragmentManager,因为它在代码中显示错误(ChildFragmentManager无法解

  • 我有这个活动,它包含一个片段。这个片段布局由一个包含多个片段(实际上是两个)的视图寻呼机组成。 当创建视图分页器时,它的适配器被创建,被调用,我的子片段被创建。太棒了。 现在,当我旋转屏幕时,框架处理片段的重新创建,适配器从主片段在我的中再次创建,但是从未被调用,因此我的适配器持有错误的引用(实际上为空),而不是两个片段。 我发现片段管理器(即子片段管理器)包含一个名为的片段数组,这当然是代码无法

  • 我的应用程序有两个项目的导航抽屉:一个ViewPager(在一个片段内)和支持Map碎片。 ViewPager使用FragmentStatePagerAdapter并返回两个片段。 当我打开抽屉菜单并选择“项目1”(支持MapFrature)时,另一个碎片(ViewPager在其中)执行,但此方法不会破坏适配器创建的碎片,所以当我再次选择“项目0”时,我的应用程序创建的ViewPager与它的两个

  • Back Stack是否支持与Android中的嵌套片段交互? 如果是这样,我做错了什么?在我的实现中,后退按钮完全忽略了我将此事务添加到后堆栈的事实。我希望这不是因为嵌套片段的问题,只是我做了一些不正确的事情。 以下代码位于我的一个片段中,用于将新片段与当前显示的任何嵌套片段交换:

  • 根据谷歌的文档: 现在可以在片段中嵌入片段。这对于各种情况都很有用,在这些情况下,您需要将动态和可重用的UI组件放置到本身是动态和可重用的UI组件中。例如,如果使用ViewPager创建左右滑动并占用大部分屏幕空间的片段,现在可以将片段插入每个片段页面。要嵌套片段,只需对要添加片段的片段调用getChildFragmentManager()。这将返回一个FragmentManager,您可以像通常