嵌套片段转换不正确 [英] Nested fragments transitioning incorrectly

本文介绍了嵌套片段转换不正确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您好,堆栈溢出的好程序员!我在这个问题上度过了愉快的一周,现在非常绝望

Hello good programmers of stack overflow! I've spent a good week with this problem and am now very desperate for a solution.

我正在使用android.app。Fragment不要与支持片段混淆。

I'm using android.app.Fragment's not to be confused with the support fragments.

我有6个子片段,分别为:

I have 6 child fragments named:


  • FragmentOne

  • FragmentTwo

  • FragmentThree

  • FragmentA

  • FragmentB

  • FragmentC

  • FragmentOne
  • FragmentTwo
  • FragmentThree
  • FragmentA
  • FragmentB
  • FragmentC

我有2个父片段,分别是:

I have 2 parent fragments named:


  • FragmentNumeric

  • FragmentAlpha

  • FragmentNumeric
  • FragmentAlpha

我有1个活动名为:


  • MainActivity

  • MainActivity

他们行为如下:


  • 子片段是仅显示视图的片段,它们不显示也不包含片段。

  • 父片段用单个子片段填充整个视图。他们可以用其他子片段替换该子片段。

  • 该活动使用父片段填充了大部分视图。它可以用其他父片段替换它。
    因此,在任何时候屏幕上都只显示一个子片段。

您可能已经猜到了,

FragmentNumeric 显示子片段 FragmentOne FragmentTwo FragmentThree

FragmentAlpha 显示子片段 FragmentA FragmentB FragmentC

我正在尝试过渡/为父级和子级片段制作动画。子片段会平稳过渡,并且符合预期。但是,当我过渡到新的父片段时,它看起来很糟糕。子片段看起来像是从其父片段运行独立的过渡。子片段看起来也像从父片段中删除了一样。您可以在 https://imgur.com/kOAotvk 上查看它的gif。请注意,当我单击显示Alpha时会发生什么。

I'm trying to transition/animate parent and child fragments. The child fragments transition smoothly and as expected. However when I transition to a new parent fragment, it looks terrible. The child fragment looks like it runs an independent transition from its parent fragment. And the child fragment looks like it is removed from the parent fragment as well. A gif of it can be viewed here https://imgur.com/kOAotvk. Notice what happens when I click Show Alpha.

最接近的问题&我可以在这里找到答案:嵌套片段在过渡动画中会消失,但是所有答案是令人不满意的骇客。

The closest question & answers I could find are here: Nested fragments disappear during transition animation however all of the answers are unsatisfying hacks.

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

I have the following animator effects (duration is long for testing purposes):

fragment_enter.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_exit.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_nothing.xml

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






MainActivity.kt



要考虑的事情:第一个父片段FragmentNumeric没有输入效果,因此它始终可以在活动中使用,也没有退出效果,因为什么都没有退出。我还使用 FragmentTransaction#add ,其中FragmentAlpha使用 FragmentTransaction#replace


MainActivity.kt

Things to consider: The first parent fragment, FragmentNumeric, doesn't have enter effects so its always ready with the activity and doesn't have exit effects because nothing is exiting. I'm also using FragmentTransaction#add with it where as FragmentAlpha is using FragmentTransaction#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()
        }
    }
}






FragmentNumeric



在快速显示其第一个子片段方面与活动具有相同的作用


FragmentNumeric

Does the same thing as the activity in terms of quickly showing its first child fragment.

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与Fragmen遵循相同的模式tNumeric,分别用片段A,B和C替换片段1,片段2和片段3。


Other Fragments

FragmentAlpha is following the same pattern as FragmentNumeric, replacing Fragments One, Two and Three with Fragments A, B and C respectively.

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

Children fragments are just showing the following XML view and setting its text and button click listener dynamically to either call a function from the parent fragment or activity.

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>

使用匕首和某些合同,我让子片段回调到其父片段并通过执行某些操作来托管活动如下所示:

Using dagger and some contracts I have the child fragments callback to their parent fragments and hosting activities by doing something like the below:

FragmentOne设置按钮单击侦听器的作用:

FragmentOne sets the button click listener to do:

(parentFragment as FragmentNumeric).showFragmentTwo()

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

FragmentTwo sets the button click listener to do:

(parentFragment as FragmentNumeric).showFragmentThree()

FragmentThree不同,它将设置点击监听器的作用:

FragmentThree is different, it will set the click listener to do:

(activity as MainActivity).showFragmentAlpha()






有人吗这个问题的解决方案?


Does anyone have a solution for this problem?

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

I've added an example project as requested: https://github.com/zafrani/NestedFragmentTransitions

与原始视频中的一个区别是父片段不再使用具有xFraction属性的视图。这样看来,Enter动画不再具有这种重叠效果。但是,它仍会从父级中删除子级片段并对其进行动画处理。动画完成后,片段3立即被片段A取代。

A difference in it and that from the one in my original video is the parent fragment no longer uses a view with the xFraction property. So it looks like the enter animation doesn't have that overlapping effect anymore. It still however does remove the child fragment from the parent and animate them side by side. After the animation completes, Fragment Three is replaces with Fragment A instantly.

子片段视图正在使用xFraction属性。关键是在父级设置动画时抑制子级动画。

Both the parent and child fragment views are using xFraction property. The key is to suppress the childs animation when the parent is animating.

推荐答案

我想我找到了一种使用Fragment#onCreateAnimator解决此问题的方法。可以在此处查看过渡的gif: https://imgur.com/94AvrW4

I think I've found a way to solve this using Fragment#onCreateAnimator. A gif of the transition can be viewed here: https://imgur.com/94AvrW4.

我进行了PR测试,到目前为止,它按预期运行,并且可以进行配置更改并支持后退按钮。这是链接 https://github.com/zafrani/NestedFragmentTransitions/ pull / 1 / files#diff-c120dd82b93c862b01c2548bdcafcb20R25

I made a PR for testing, so far it's working as I expect and surviving configuration changes and supporting the back button. Here is the link https://github.com/zafrani/NestedFragmentTransitions/pull/1/files#diff-c120dd82b93c862b01c2548bdcafcb20R25

Parent和Child片段的BaseFragment都针对onCreateAnimator()

The BaseFragment for both the Parent and Child fragments is doing this for 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中更容易看到。

The booleans are being set in different scenarios that are easier to see in the 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)
    }

万一有人发现更好的方法,这个问题将悬而未决。

Will leave the question open incase someone finds a better way.

这篇关于嵌套片段转换不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆