78

FragmentTransaction.setCustomAnimationsを使用して次の効果を達成しようとしています。

  1. フラグメントAが表示されています
  2. フラグメントAをフラグメントBに置き換えます。フラグメントAは、交換中も表示されたままになります。フラグメントBは右からスライドインするはずです。フラグメントBは、フラグメントAの上部にスライドする必要があります。

アニメーション設定でスライドを取得するのに問題はありません。私の問題は、アニメーションのスライドが実行されている間、フラグメントAをそのままにして、フラグメント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();

ご覧のとおり、「アウト」アニメーションにはアニメーションR.anim.nothingを定義しました。これは、実際には、フラグメントAに、トランザクション中の場所に留まる以外のことをさせたくないためです。

アニメーションリソースは次のとおりです。

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" />

none.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" />
4

12 に答える 12

28

更新(2020年6月16日)

フラグメントライブラリから始めて、1.2.0この問題を修正するための推奨される方法は、で使用FragmentContainerViewすることFragmentTransaction.setCustomAnimations()です。

ドキュメントによると:

終了アニメーションを使用するフラグメントは、FragmentContainerViewの他のすべてのフラグメントよりも先に描画されます。これにより、既存のフラグメントがビューの上部に表示されなくなります。

この問題を修正する手順は次のとおりです。

  1. フラグメントライブラリを1.2.0以上に更新しますandroidx.fragment:fragment:1.2.0;
  2. xmlフラグメントコンテナ(、、<fragment>または<FrameLayout>else)を<androidx.fragment.app.FragmentContainerView>;に置き換えます。
  3. FragmentTransaction.setCustomAnimations()フラグメントの遷移をアニメーション化するために使用します。

前の回答(2015年11月19日)

Lollipopから始めて、入力するフラグメントのdetranslationZを増やすことができます。既存のものの上に表示されます。

例えば:

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    ViewCompat.setTranslationZ(getView(), 100.f);
}

アニメーションの期間中のみ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;
}
于 2015-11-19T23:25:13.847 に答える
20

まだ答えが必要かどうかはわかりませんが、最近同じことをする必要があり、あなたが望むことをする方法を見つけました。

私はこのようなものを作りました:

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に表示します。

それはうまく機能しますが、古いフラグメントはまだ私のビューにあります、私が置くならば、私はアンドロイドに彼が望むようにそれを管理させます:

ft.remove(myolderFrag);

アニメーション中は表示されません。

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>
于 2012-12-13T11:53:03.843 に答える
8

私は自分に合った解決策を見つけました。私はFragmentStatePagerAdapterでViewPagerを使用することになりました。ViewPagerは、スワイプ動作とフラグメント内のFragmentStatePagerAdapterスワップを提供します。着信ページの「下」に1つのページを表示する効果を実現するための最後の秘訣は、PageTransformerを使用することです。PageTransformerは、ViewPagerのページ間のデフォルトの遷移をオーバーライドします。これは、左側のページで変換と少量のスケーリングで効果を実現するPageTransformerの例です。

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);
            }
        }
    }

}
于 2013-01-11T05:18:55.270 に答える
6

さらに実験を重ねた後(したがって、これが私の2番目の答えです)、問題は、R.anim.nothingが「stayput」と明示的に言う別のアニメーションが必要なときに「消える」という意味ではないことです。解決策は、次のような真の「何もしない」アニメーションを定義することです。

ファイルを作成します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();
于 2015-02-23T02:34:58.677 に答える
2

私は、これまでの提案よりもエレガントであると思う代替ソリューション(十分にテストされていない)を見つけました:

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クラスの内部クラス内から呼び出されています。

新しいフラグメントが挿入され、アニメーションが完了してから、古いフラグメントが削除されます。

一部のアプリケーションでは、これにいくつかのメモリの問題がある可能性がありますが、両方のフラグメントを無期限に保持するよりも優れています。

于 2014-11-06T21:12:06.057 に答える
1

私の実装を添付するjfriteの回答に基づく

import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewCompat;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.util.Log;

public final class AnimationHelper {
    private AnimationHelper() {

    }

    private static String TAG = AnimationHelper.class.getSimpleName();
    private static final float ELEVATION_WHILE_ENTER_ANIMATION_IS_RUNNING = 100f;
    private static final int RESTORE_ANIMATION_DELAY = 16;

    /**
     * When replacing fragments with animations, by default the new fragment is placed below the replaced fragment. This
     * method returns an animation object that sets high elevation at the beginning of the animation and resets the
     * elevation when the animation completes. The {@link Animation} object that is returned is not the actual object
     * that is used for the animating the fragment but the callbacks are called at the appropriate time. The method
     * {@link Fragment#onCreateAnimation(int, boolean, int)} by default returns null, therefor, this method can be used
     * as the return value for {@link Fragment#onCreateAnimation(int, boolean, int)} method although it can return
     * null.
     * @param enter True if fragment is 'entering'.
     * @param nextAnim Animation resource id that is about to play.
     * @param fragment The animated fragment.
     * @return If nextAnim is a valid resource id and 'enter' is true, returns an {@link Animation} object with the
     * described above behavior, otherwise returns null.
     */
    @Nullable
    public static Animation increaseElevationWhileAnimating(boolean enter, int nextAnim,
                                                            @NonNull Fragment fragment) {
        if (!enter || nextAnim == 0) {
            return null;
        }
        Animation nextAnimation;
        try {
            nextAnimation = AnimationUtils.loadAnimation(fragment.getContext(), nextAnim);
        } catch (Resources.NotFoundException e) {
            Log.e(TAG, "Can't find animation resource", e);
            return null;
        }
        nextAnimation.setAnimationListener(new Animation.AnimationListener() {
            private float oldTranslationZ;

            @Override
            public void onAnimationStart(Animation animation) {
                if (fragment.getView() != null && !fragment.isDetached()) {
                    oldTranslationZ = ViewCompat.getTranslationZ(fragment.getView());
                    ViewCompat.setTranslationZ(fragment.getView(), ELEVATION_WHILE_ENTER_ANIMATION_IS_RUNNING);
                }
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                if (fragment.getView() != null && !fragment.isDetached()) {
                    fragment.getView().postDelayed(() -> {
                        // Decreasing the elevation at the ned can cause some flickering because of timing issues,
                        // Meaning that the replaced fragment did not complete yet the animation. Resting the animation
                        // with a minor delay solves the problem.
                        if (!fragment.isDetached()) {
                            ViewCompat.setTranslationZ(fragment.getView(), oldTranslationZ);
                        }
                    }, RESTORE_ANIMATION_DELAY);
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        return nextAnimation;
    }
}

フラグメントを形成するヘルパーを使用する方法は次のとおりです。

@Override
    public Animation onCreateAnimation(int transit, final boolean enter, int nextAnim) {
        return AnimationHelper.increaseElevationWhileAnimating(enter, nextAnim, this);
    }

これが私がアニメーションでフラグメントを始める方法です

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.setCustomAnimations(R.anim.slide_in, R.anim.hold, R.anim.hold, R.anim.slide_out);
于 2019-01-21T09:03:36.290 に答える
1

かなり多くのステップがありますが、トランジションとアニメーションを組み合わせてこれを行うことができました

 supportFragmentManager
         .beginTransaction()
         .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN
         .setCustomAnimations(R.anim.nothing, R.anim.scale_out)
         .replace(R.id.fragment_container, fragment)
         .commit()

Nothing.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@integer/transition_animation_time" />

スケールアウト-途中で古いフラグメントをアニメーション化します

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:fromXScale="100%"
    android:fromYScale="100%"
    android:toXScale="90%"
    android:toYScale="90%"
    android:pivotY="50%"
    android:pivotX="50%"/>

Fragment.kotlin

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val inflater = TransitionInflater.from(requireContext())
        enterTransition = inflater.inflateTransition(R.transition.slide)
    }

res / transition/slide.xml-途中で新しいフラグメントをアニメーション化します

<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:slideEdge="bottom" />
于 2021-02-05T15:38:05.487 に答える
0
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();
于 2016-09-17T12:55:11.673 に答える
0

これは、興味のある人のための私の現在の回避策です。

新しいを追加するための関数でFragment

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を使用したいのですが、のようなフラグメントに関連付けられていないトランザクションアニメーションの終了を検出するために使用できる方法はありませんでしたonCreateAnimation()。適切なリスナーによる提案/編集をいただければ幸いです。

この方法で追加してFragmentいるのは軽量であるため、上にあるフラグメントと一緒にフラグメントコンテナに入れても問題ありません。

フラグメントを削除したい場合fragmentManager.beginTransaction().remove(toRemove).commitAllowingStateLoss();は、Handler'sRunnableOnBackStackChangedListener:に入れることができます

// 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();
}

上記の解決策はコンテナの最初のフラグメントでは機能しないことに注意してください(バックスタックにないため)。そのため、別の方法でそのフラグメントを復元する必要があります。おそらく、最初のフラグメントへの参照を何らかの方法で保存します。 。ただし、バックスタックを使用せず、常にフラグメントを手動で置き換える場合、これは問題ではありません。または、すべてのフラグメントをバックスタック(最初のフラグメントを含む)に追加し、オーバーライドonBackPressedして、バックスタックにフラグメントが1つだけ残っているときに空白の画面を表示する代わりに、アクティビティが終了するようにすることもできます。

編集: 私はおそらくFragmentTransaction.remove()それFragmentTransaction.add()以上を置き換えることができる次の関数を発見しました:

FragmentTransactiondetach()

指定されたフラグメントをUIから切り離します。これは、バックスタックに配置されたときと同じ状態です。フラグメントはUIから削除されますが、その状態はフラグメントマネージャーによって引き続きアクティブに管理されています。この状態になると、そのビュー階層が破棄されます。

FragmentTransactionattach()

以前にdetach(Fragment)を使用してUIからデタッチした後、フラグメントを再アタッチします。これにより、ビュー階層が再作成され、UIにアタッチされ、表示されます。

于 2016-11-12T20:15:43.497 に答える
0

レイアウトに立面図を追加します。

30spの下部からスライドするフラグメントに標高を追加すると、機能しました。

私はここで提案された多くの解決策を試しました。これは、すべてのアイデアと標高の追加を組み合わせた完全なコードと出力です。

出力:

標高なし:

ここに画像の説明を入力してください

標高あり:

ここに画像の説明を入力してください

完全なコード:

ルートに標高を追加します

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:elevation="30sp">
     ----

     ----
 </RelativeLayout>

フラグメントを下からスライドさせる方法は?

getSupportFragmentManager()
            .beginTransaction()
            .setCustomAnimations(R.anim.slide_in_bottom, R.anim.do_nothing, R.anim.do_nothing, R.anim.slide_out_bottom)
            .replace(R.id.fragmentContainer, currentFragment, "TAG")
            .addToBackStack("TAG")
            .commit();

戻るボタンが押されたときに逆を行うにはどうすればよいですか?

getSupportFragmentManager()
            .popBackStack();

setCustomAnimations()メソッドでアニメーションの開始と終了をすでに定義しているので 。呼び出しpopBackStack();は逆アニメーションを処理します。

R.anim.slide_in_bottom

<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
       android:duration="500"
       android:fromYDelta="100%"
       android:toYDelta="0%">
    </translate>
</set>

R.anim.slide_out_bottom

<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="500"
        android:fromYDelta="0%"
        android:toYDelta="100%">
    </translate>
</set>

R.anim.do_nothing

<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="500"/>
     <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromAlpha="1.0"
        android:toAlpha="1.0"
        android:duration="500"
        android:startOffset="500" />
</set>
于 2020-06-12T23:28:53.947 に答える
0

フラグメントマネージャーを使用する場合の回答

   getSupportFragmentManager()
            .beginTransaction()
            .setCustomAnimations(
                R.anim.slide_in_from_left,
                R.anim.slide_free_animation,
                R.anim.slide_free_animation,
                R.anim.slide_out_to_left
            )
            .replace(R.id.fragmentContainer, currentFragment, "TAG")
            .addToBackStack("TAG")
            .commit()

ナビゲーションを使用する場合の回答:

     <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment"
            app:enterAnim="@anim/slide_in_from_left"
            app:exitAnim="@anim/slide_free_animation"
            app:popEnterAnim="@anim/slide_free_animation"
            app:popExitAnim="@anim/slide_out_to_left" />

slide_in_from_left.xml

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

    <translate
        android:duration="500"
        android:fromXDelta="-100%p"
        android:fromYDelta="0%"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="0%"
        android:toYDelta="0%" />

</set>

slide_out_to_left.xml

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

    <translate
        android:duration="500"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:toXDelta="-100%p"
        android:toYDelta="0%" />

</set>

slide_free_animation.xml

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

    <alpha
        android:duration="300"
        android:fromAlpha="1.0"
        android:startOffset="200"
        android:toAlpha="0.0" />

</set>
于 2021-02-05T08:45:54.800 に答える
0

私は同じ問題を抱えており、すべての解決策はやり過ぎのように見えます。フラグメントを非表示にすると、アニメーションの終了後にフラグメントを削除する必要があるときにエラーが発生しますが、いつ発生するかはわかりません。


私は最も簡単な解決策を見つけました:トランザクションでネガティブな前のフラグメントに設定するだけですtranslationZ

childFragmentManager.fragments.lastOrNull()?.let { it.view?.translationZ = -1f }

トランザクション全体は次のようになります。

childFragmentManager.commit {
    setCustomAnimations(...)
    childFragmentManager.fragments.lastOrNull()?.let { it.view?.elevation = -1f }
    replace(..., ...)
    addToBackStack(...)
}

それで全部です。

ビューの動作に影響を与えることはありません。すべてが通常どおりに機能するはずです。バックするアニメーションも正しく機能します(正しい順序で)。

于 2021-02-16T16:27:45.573 に答える