17

你好程序員的堆棧溢出!我已經花了一週的時間解決了這個問題,現在我非常想要絕望嵌套片段轉換不正確


場景

我使用android.app.Fragment不與載體片段相混淆。

我有6個孩子命名片段:

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

我已2母體片段命名爲:

  • FragmentNumeric
  • FragmentAlpha

我已經1個活性命名爲:

  • MainActivity

他們的表現在以下幾點:

  • 兒童片段片段只顯示一個視圖,他們不顯示,也不包含片段。
  • 父級片段使用單個子片段填充其整個視圖。他們能夠將子片段替換爲其他子片段。
  • 該活動使用父級片段填充其大部分視圖。它可以將其替換爲其他父代片段。 因此,在任何時候屏幕只顯示一個孩子片段。

正如你可能已經猜到,

FragmentNumeric顯示子片段FragmentOneFragmentTwoFragmentThree

FragmentAlpha顯示子片段FragmentA,FragmentBFragmentC


我試圖轉型/動畫家長和孩子碎片問題。如預期的那樣,孩子的片段順利過渡。但是當我轉換到一個新的父代片段時,它看起來很糟糕。子片段看起來像從其父片段運行獨立轉換。子片段看起來也是從父片段中移除的。它的一個GIF可以在這裏查看https://imgur.com/kOAotvk。注意當我點擊Show Alpha時會發生什麼。

最接近的問題&答案我可以在這裏找到:Nested fragments disappear during transition animation但是,所有的答案都令人不滿。


動畫XML文件

我有以下的動畫效果(持續時間長用於測試目的):

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,沒有進入影響所以它總是準備好與活動,並沒有退出的影響,因爲沒有什麼是退出。其中,作爲FragmentAlpha在迅速顯示出它的第一個孩子片段條款使用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

做同樣的事情作爲活動我還使用FragmentTransaction#add它。

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.替換片段一,二及三。

兒童片段只是顯示以下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不同的是,它會設置的點擊收聽做:

(activity as MainActivity).showFragmentAlpha() 

有沒有人有這個問題的解決方案?


更新1

我添加了一個例子項目的要求:https://github.com/zafrani/NestedFragmentTransitions

從一個在我的原始視頻在它和差變成父片段不再使用視圖與xFraction屬性。所以看起來輸入動畫不再有重疊效果。但它仍然會從父級刪除子級片段並將它們並排設置。動畫完成後,Fragment Three即刻替換爲Fragment A。

更新2

父級和子級片段視圖都使用xFraction屬性。關鍵是在父母動畫時抑制孩子動畫。

+3

這個問題已經非常明確,但由於您似乎有一個乾淨的項目來顯示問題,如果您可以將它上載到某個位置,它將會很酷。 – natario

+0

請更新GIF,看起來不見了。 UPD。對不起,我的壞,找到了圖像。 – GensaGames

+0

@natario我已經添加了一個github鏈接。 – zafrani

回答

4

我想我已經找到了一種方法來解決這個使用Fragment#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) 
    } 

將離開的問題開放櫃面有人發現了一個更好的辦法。

0

你得到如此有線的結果,因爲你正在使用你的片段退出動畫。基本上,我們每次都有Fragment動畫的問題,並最終從源片段動畫轉移到使用自己,每次我們執行轉換時。

1)要驗證此行爲,您可以刪除片段的退出動畫,並且一切都會正常。在大多數情況下,它應該是足夠的,原因退出動畫非常具體,只爲單個片斷管理使用(不以你的情況下,與孩子的)

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

2)另一種選擇,這可能適合,這是考慮應用程序的結構並儘量避免用動畫替換片段。如果您需要替換,請勿使用動畫,並且您可以添加任何動畫(包括進入和退出),但僅限於添加。