FragmentTransactionanimation滑过顶部
我正在尝试使用FragmentTransaction.setCustomAnimations实现以下效果。
- 片段A正在显示
- 用碎片Breplace碎片A.碎片A应该在replace期间保持可见。 片段B应该从右侧滑入。 片段B应该在片段A的顶部滑动。
我没有问题在animation设置中获取幻灯片。 我的问题是,我无法弄清楚如何使片段A保持原状,并且在animation中的幻灯片正在运行时处于片段B下。 无论我做什么,似乎碎片A是最重要的。
我怎样才能做到这一点?
这是FragmentTransaction代码:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.setCustomAnimations(R.anim.slide_in_right, R.anim.nothing, R.anim.nothing, R.anim.slide_out_right); ft.replace(R.id.fragment_content, fragment, name); ft.addToBackStack(name); ft.commit();
正如你所看到的,我已经为“out”animation定义了一个animationR.anim.nothing,因为我实际上不希望片段A做任何事情,除了在事务中保持它的位置。
这里是animation资源:
slide_in_right.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@android:integer/config_mediumAnimTime" android:fromXDelta="100%p" android:toXDelta="0" android:zAdjustment="top" />
nothing.xml
<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@android:integer/config_mediumAnimTime" android:fromAlpha="1.0" android:toAlpha="1.0" android:zAdjustment="bottom" />
我不知道你是否仍然需要一个答案,但我最近还需要这样做,我find了一个方法来做你想要的。
我做了这样的事情:
FragmentManager fm = getFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); MyFragment next = getMyFragment(); ft.add(R.id.MyLayout,next); ft.setCustomAnimations(R.anim.slide_in_right,0); ft.show(next); ft.commit();
我在一个FrameLayout中显示我的片段。
它工作的罚款,但旧的片段仍然在我看来,我让androidpipe理它,因为他想要的,因为如果我把:
ft.remove(myolderFrag);
它在animation期间不显示。
slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="150" android:fromXDelta="100%p" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0" /> </set>
我find了一个适合我的解决scheme。 我最终使用了一个带有FragmentStatePagerAdapter的ViewPager。 ViewPager提供了划片行为和片段中的FragmentStatePagerAdapter交换。 实现在传入页面下方显示一个页面效果的最后一招是使用PageTransformer。 PageTransformer重写ViewPager在页面之间的默认转换。 下面是一个PageTransformer的例子,它可以在左边的页面上实现翻译和less量缩放的效果。
public class ScalePageTransformer implements PageTransformer { private static final float SCALE_FACTOR = 0.95f; private final ViewPager mViewPager; public ScalePageTransformer(ViewPager viewPager) { this.mViewPager = viewPager; } @SuppressLint("NewApi") @Override public void transformPage(View page, float position) { if (position <= 0) { // apply zoom effect and offset translation only for pages to // the left final float transformValue = Math.abs(Math.abs(position) - 1) * (1.0f - SCALE_FACTOR) + SCALE_FACTOR; int pageWidth = mViewPager.getWidth(); final float translateValue = position * -pageWidth; page.setScaleX(transformValue); page.setScaleY(transformValue); if (translateValue > -pageWidth) { page.setTranslationX(translateValue); } else { page.setTranslationX(0); } } } }
从棒棒糖开始,你可以增加input片段的de translationZ。 它会出现在现有的上面。
例如:
@Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); ViewCompat.setTranslationZ(getView(), 100.f); }
如果您只想在animation期间修改translationZ值,则应该这样做:
@Override public Animation onCreateAnimation(int transit, final boolean enter, int nextAnim) { Animation nextAnimation = AnimationUtils.loadAnimation(getContext(), nextAnim); nextAnimation.setAnimationListener(new Animation.AnimationListener() { private float mOldTranslationZ; @Override public void onAnimationStart(Animation animation) { if (getView() != null && enter) { mOldTranslationZ = ViewCompat.getTranslationZ(getView()); ViewCompat.setTranslationZ(getView(), 100.f); } } @Override public void onAnimationEnd(Animation animation) { if (getView() != null && enter) { ViewCompat.setTranslationZ(getView(), mOldTranslationZ); } } @Override public void onAnimationRepeat(Animation animation) { } }); return nextAnimation; }
经过更多的实验(因此这是我的第二个答案),问题似乎是,当我们想要另一个明确表示“保持放置”的animation时,R.anim.nothing就意味着“消失”。 解决的办法是定义一个真正的“什么都不做”的animation,如下所示:
创build文件no_animation.xml
:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:interpolator="@android:anim/linear_interpolator" android:fromXScale="1.0" android:toXScale="1.0" android:fromYScale="1.0" android:toYScale="1.0" android:duration="200" /> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="200" android:startOffset="200" /> </set>
现在简单地做,否则:
getActivity().getSupportFragmentManager().beginTransaction() .setCustomAnimations(R.anim.slide_in_right, R.anim.no_animation) .replace(R.id.container, inFrag, FRAGMENT_TAG) .addToBackStack("Some text") .commit();
我find了一个替代解决scheme(没有经过严格testing),我发现比以前的build议更优雅:
final IncomingFragment newFrag = new IncomingFragment(); newFrag.setEnterAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { clearSelection(); inFrag.clearEnterAnimationListener(); getFragmentManager().beginTransaction().remove(OutgoingFragment.this).commit(); } @Override public void onAnimationRepeat(Animation animation) { } }); getActivity().getSupportFragmentManager().beginTransaction() .setCustomAnimations(R.anim.slide_in_from_right, 0) .add(R.id.container, inFrag) .addToBackStack(null) .commit();
这是从OutgoingFragment类的内部类中调用的。
正在插入新片段,animation完成,然后旧片段被删除。
这在某些应用程序中可能会有一些内存问题,但是要比无限期地保留这两个片段更好。
FragmentTransaction ft = ((AppCompatActivity) context).getSupportFragmentManager().beginTransaction(); ft.setCustomAnimations(0, R.anim.slide_out_to_right); if (!fragment.isAdded()) { ft.add(R.id.fragmentContainerFrameMyOrders, fragment); ft.show(fragment); } else ft.replace(R.id.fragmentContainerFrameMyOrders, fragment); ft.commit();
这是我目前对任何感兴趣的人的解决方法。
在添加新Fragment
的function中:
final Fragment toRemove = fragmentManager.findFragmentById(containerID); if (toRemove != null) { new Handler().postDelayed(new Runnable() { @Override public void run() { fragmentManager.beginTransaction().hide(toRemove).commit(); } }, getResources().getInteger(android.R.integer.config_mediumAnimTime) + 100); // Use whatever duration you chose for your animation for this handler // I added an extra 100 ms because the first transaction wasn't always // fast enough } fragmentManager.beginTransaction() .setCustomAnimations(enter, 0, 0, popExit).add(containerID, fragmentToAdd) .addToBackStack(tag).commit();
并在onCreate
:
final FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.addOnBackStackChangedListener( new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { Fragment current = fragmentManager.findFragmentById(containerID); if (current != null && current.isHidden()) { fragmentManager.beginTransaction().show(current).commit(); } } });
我更喜欢某种AnimationListener而不是上面的Handler,但是我没有看到任何方法可以使用它来检测事件animation的结尾,而不像onCreateAnimation()
那样绑定到片段。 任何build议/编辑与适当的倾听者将不胜感激。
我将指出我以这种方式添加的Fragment
是轻量级的,所以将它们放在片段容器中以及片段之上并不是一个问题。
如果你想删除片段,你可以把fragmentManager.beginTransaction().remove(toRemove).commitAllowingStateLoss();
在Handler
的Runnable
,在OnBackStackChangedListener
:
// Use back stack entry tag to get the fragment Fragment current = getCurrentFragment(); if (current != null && !current.isAdded()) { fragmentManager.beginTransaction() .add(containerId, current, current.getTag()) .commitNowAllowingStateLoss(); }
请注意,上面的解决scheme不适用于容器中的第一个片段(因为它不在后面的堆栈中),所以你将不得不有另一种方式来恢复,也许保存一个引用到第一个片段..但是如果你不使用back栈,并且总是手动replacefragment,这不是问题。 或者,您可以将所有片段添加到后端堆栈(包括第一个片段)并覆盖onBackPressed
,以确保您的活动退出,而不是在后端堆栈中只剩下一个片段时显示空白屏幕。
编辑:我发现了以下function,可能会取代上述的FragmentTransaction.remove()
和FragmentTransaction.add()
:
FragmentTransaction
。 detach() :
从UI中分离给定的片段。 这与将其放在后端堆栈上时的状态相同:将片段从UI中删除,但其状态仍由片段pipe理器主动pipe理。 进入这个状态时,它的视图层次被破坏。
FragmentTransaction
。 attach() :
之前用分离(Fragment)从UI分离后再重新附加一个片段。 这将导致其视图层次被重新创build,附加到UI并显示。