大家好,我是来自 Stack Overflow 的优秀程序员!我已经花了一个星期的时间来解决这个问题,现在非常绝望,希望能得到解决。
场景
我正在使用 android.app.Fragment,不要与 support fragments 混淆。
我有6个子片段,分别为:
FragmentOne
FragmentTwo
FragmentThree
FragmentA
FragmentB
FragmentC
我有2个父片段,分别为:
FragmentNumeric
FragmentAlpha
它们的行为如下:
- 子片段仅显示视图,不显示也不包含其他片段。
- 父片段填充其整个视图并显示单个子片段。 它们可以用其他子片段替换子片段。
- 活动将其大部分视图填充为父片段。 它可以用其他父片段替换它。 因此,在任何一时刻,屏幕上只显示一个子片段。
正如您可能猜到的那样,
FragmentNumeric
显示子片段 FragmentOne
、FragmentTwo
和 FragmentThree
。
FragmentAlpha
显示子片段 FragmentA
、FragmentB
和 FragmentC
。
问题
我正在尝试过渡/动画父片段和子片段。 子片段过渡平滑,效果如预期。 但是当我转换到新的父片段时,它看起来很糟糕。 子片段看起来像从其父片段独立运行过渡一样。 子片段还似乎已从其父片段中移除。 可以在此处查看其 GIF 动画:https://imgur.com/kOAotvk。 注意单击“Show Alpha”时发生的情况。
我能找到的最接近的问题和答案在这里:Nested fragments disappear during transition animation,但所有答案都是不令人满意的 hack。
Animator XML Files
我有以下动画效果(为了测试目的,持续时间很长):
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
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
该片段与活动相同,在快速显示其第一个子片段方面具有相同的作用。
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相同的模式,将Fragments One、Two和Three分别替换为Fragments 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>
使用 Dagger 和一些协议,我让子片段通过以下方式回调到其父片段和宿主活动:
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 属性。关键在于在父项动画时抑制子项的动画。