web-dev-qa-db-ja.com

ポップアニメーションを再生せずにフラグメントバックスタックをポップする

次のコードを使用して、フラグメントスタックにフラグメントをプッシュします。

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right,
     R.anim.slide_in_left, R.anim.slide_out_left);
fragmentTransaction.replace(getId(), newFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

このように、フラグメントスタックがポップされると、たとえば戻るボタンを押すと、フラグメントポップアニメーションが再生されます。ただし、このアニメーションを表示せずにフラグメントのバックスタックをポップしたい状況があります。別のアクティビティから戻ったばかりで、アニメーションなしで前のフラグメントを一度に表示したいからです。

ナビゲーションの例は次のようになります。

  • ユーザーは、ルートフラグメントでスタート画面にいます
  • 彼はルートフラグメント上のアイテムを選択し、新しいフラグメントを表示してそのアイテムの詳細を表示します。これは、プッシュケースとポップケースの両方にアニメーションを設定するフラグメントトランザクションを使用して行われます(したがって、ユーザーが戻るボタンを押すと、トランジションがアニメーション化されます)
  • このフラグメントから、彼は(何らかの理由で)表示されたばかりのアイテムを削除するアクティビティを開始します
  • このアクティビティが終了したら、「詳細フラグメント」の「ポップアニメーション」を表示せずにルートフラグメントに戻りたい

指定したポップアニメーションを再生せずにフラグメントバックスタックをポップする方法はありますか?

53
ChristophK

だから、Warpzitは正しい軌道に乗っていた、彼はあなたの特定の問題にあまりうまく対処しなかった。私はまったく同じ問題に出くわしましたが、ここでそれを解決しました。

まず、静的なブール変数を作成しました(簡単にするために、FragmentUtilsクラスに入れます)...

public class FragmentUtils {
    public static boolean sDisableFragmentAnimations = false;
}

次に、持っているすべてのフラグメントで、onCreateAnimationメソッドをオーバーライドする必要があります...

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if (FragmentUtils.sDisableFragmentAnimations) {
        Animation a = new Animation() {};
        a.setDuration(0);
        return a;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}

その後、アクティビティからバックスタックをクリアする必要がある場合、次のことを行うだけです...

public void clearBackStack() {
    FragmentUtils.sDisableFragmentAnimations = true;
    getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    FragmentUtils.sDisableFragmentAnimations = false;
}

出来上がり、clearBackStack()を呼び出すと、遷移アニメーションなしでルートフラグメントに戻ります。

将来的には、大きなGがこれを行う愚かな方法を追加することを願っています。

86
Geoff

サポートライブラリの場合、次のように機能します。

カスタムポップアニメーションが必要なフラグメントでは、onCreateAnimationを独自のカスタムアニメーションでオーバーライドします。あなたはそれを取得し、あなたが望むものに応じてある種のパラメータを設定することができます。通常のフラグメントで動作させるには、追加の作業が必要になる場合があります。

これをオーバーライドして設定期間を変更する例を次に示します。

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    Animation anim = (Animation) super.onCreateAnimation(transit, enter, nextAnim);
    if(!enter) {
        if(anim != null) {
            anim.setDuration(0); // This doesn't seem to be called.
            return anim;
        } else {
            Animation test = new TestAnimation();
            test.setDuration(0);
            return test;
        }
    }
    return anim;
}

private class TestAnimation extends Animation {

}
6
Warpzit

ユーザーは、ルートフラグメントでスタート画面にいます

ルートフラグメントがアクティビティAに含まれているとします。

彼はルートフラグメント上のアイテムを選択し、新しいフラグメントを表示してそのアイテムの詳細を表示します。これは、プッシュケースとポップケースの両方にアニメーションを設定するフラグメントトランザクションを使用して行われます(したがって、ユーザーが戻るボタンを押すと、トランジションがアニメーション化されます)

トランザクションはバックスタックに追加されます。つまり、詳細フラグメントから戻るボタンを押すと、ポッププロセスがアニメーション化されます。

このフラグメントから、彼は(何らかの理由で)表示されたアイテムを削除するアクティビティを開始します。

アクティビティBとしましょう

このアクティビティが終了したら、「詳細フラグメント」の「ポップアニメーション」を表示せずにルートフラグメントに戻りたい

この動作を取得する1つの方法は、アクティビティBでこれを実行することです。

Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();

これにより、アクティビティAが開始され、 documentation に従ってルート状態にリセットされます(「この起動モードは、FLAG_ACTIVITY_NEW_TASKと組み合わせて使用​​することもできます: ...... ")

この構成では、デフォルトの場合にアニメーションが存在しますが、特別な場合には、次を使用してアニメーションを制御できます。

intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

アニメーションなしで新しいアクティビティを開始します。アニメーションが必要な場合は、overridePendingTransitionメソッドを使用して実行できます。

6
500865

それで、@ Geoffの答えに小さな変更を提案したいと思います。

グローバルな静的ブール値を使用する代わりに、ローカルの非静的ブール値を使用します。これが私が思いついたものです。

インターフェースを作成する

public interface TransitionAnimator {
    void disableTransitionAnimation();
    void enableTransitionAnimation();
}

フラグメントにそのインターフェースを実装させます。

public class MyFragment extends Fragment implements TransitionAnimator {

    private boolean mTransitionAnimation;

    @Override
    public void disableTransitionAnimation() {
        mTransitionAnimation = false;
    }

    @Override
    public void enableTransitionAnimation() {
        mTransitionAnimation = true;
    }

    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        Animation result;
        if (!mTransitionAnimation) {
            Animation dummyAnimation = new Animation() {
            };
            dummyAnimation.setDuration(0);
            result = dummyAnimation;
        } else {
            result = super.onCreateAnimation(transit, enter, nextAnim);
        }
        return result;
    }

そして、フラグメントの遷移アニメーションを無効にしたい場合は、

if (fragment instanceof TransitionAnimator) {
    ((TransitionAnimator) fragment).disableTransitionAnimation();
}

それらを有効にするには、ただ

if (fragment instanceof TransitionAnimator) {
    ((TransitionAnimator) fragment).enableTransitionAnimation();
}

フラグメントマネージャーのすべてのフラグメントに対して同じことを行いたい場合は、

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
    if (fragment instanceof TransitionAnimator) {
        // disable animations
        ((TransitionAnimator) fragment).disableTransitionAnimation();
    }
}

非常によく似ていますが、静的フィールドはありません。

1
Joao Sousa

実際、Androidには@Geoffの回避策に答えずにこれを行う方法があります。

fragmentsを拡張するときに、popBackStack()でアニメーションが実行されないようにするには、fragmentTransaction.setReorderingAllowed(true)を追加します。

たとえば、次のとおりです。

_supportFragmentTransaction.beginTransaction()
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .setCustomAnimations(
        Android.R.anim.fade_in,
        Android.R.anim.fade_out,
        Android.R.anim.fade_in,
        Android.R.anim.fade_out
    )
    .replace(yourContainer.id, yourFragment)
    .commit()
_

setReorderingAllowed(true)を設定すると、ポップアニメーションは再生されなくなります。結果は、実際には@Geoffの答えの結果に似ています。

0

これは@Geoffの優れた答えのフォローアップですが、より動的で現実的なシナリオに適合しています。

これは素敵な小さな投稿だと思っていましたが、今では少し手に負えなくなっていることに気付きました。ただし、コードはすべて揃っており、移行アニメーションを無効にする方法以外にも多くのことをカバーしていますが、非常に便利です。

通常、フラグメントを使用する場合、BaseActivityCallbackにアタッチするBaseFragmentが必要です。このBaseActivityCallbackは、フラグメントによって使用され、その上に新しいフラグメントを追加したり、その下にフラグメントをポップしたりすることもできます。そのため、ポップアニメーションを無効にしたいという要望があります-またはポップサイレント

interface BaseActivityCallback
{
    void addFragment ( BaseFragment f, int containerResId );
    void popFragment ( boolean silently );
}

class BaseActivity extends Android.support.v4.app.FragmentActivity implements BaseActivityCallback
{
    public void addFragment ( BaseFragment f, int containerResId )
    {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.enter, R.anim.pop_exit); // http://stackoverflow.com/a/17488542/2412477
        ft.addToBackStack(DEFAULT_FRAGMENT_STACK_NAME);
        ft.replace(containerResId, fragment);
        ft.commitAllowingStateLoss();
    }        

    public void popFragment ( boolean silently )
    {
        FragmentManager fm = getSupportFragmentManager();                
        if ( silently ) {
            int count = fm.getFragments().size();
            BaseFragment f = (BaseFragment)fm.getFragments().get(count-1);
            f.setDisableTransitionAnimations(true);
        }
        fm.popBackStackImmediate();
    }
}

public abstract class BaseFragment extends Android.support.v4.app.Fragment
{
    private static final String TAG = "BaseFragment";
    private final String STATE_DISABLE_TRANSITION_ANIMATIONS = TAG+".stateDisableTransitionAnimations";

    protected BaseActivityCallback baseActivityCallback;
    private boolean disableTransitionAnimations;    

    @Override
    public void onCreate ( @Nullable Bundle savedInstanceState )
    {
        super.onCreate(savedInstanceState);
        disableTransitionAnimations = (savedInstanceState==null ? false : savedInstanceState.getBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, false));
    }

    @Override
    public void onAttach ( Context context )
    {
        super.onAttach(context);
        baseActivityCallback = (BaseActivityCallback)context;
    }

    @Override
    public void onSaveInstanceState ( Bundle outState )
    {
        super.onSaveInstanceState(outState);
        outState.putBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, disableTransitionAnimations);
    }

    @Override
    public Animation onCreateAnimation ( int transit, boolean enter, int nextAnim )
    {
        if ( disableTransitionAnimations ) {
            Animation nop = new Animation(){};
            nop.setDuration(0);
            return nop;
        }
        return super.onCreateAnimation(transit, enter, nextAnim);
    }

    public void setDisableTransitionAnimations ( boolean disableTransitionAnimations )
    {
        this.disableTransitionAnimations = disableTransitionAnimations; // http://stackoverflow.com/a/11253987/2412477
    }
}

MainActivityを作成し、Fragment1を表示させることで、別のFragment2を追加し、Fragment1を静かにポップすることができます

public class MainActivity extends BaseActivity
{
    protected void onCreate ( Bundle savedInstanceState )
    {
        setContentView(R.layout.main_activity);
        ...
        if ( getSupportFragmentManager().getFragments() != null && !getSupportFragmentManager().getFragments().isEmpty() ) {

            addFragment( FragmentA.newInstance(), R.id.main_activity_fragment_container );
        }
    }
    ...
}

public class FragmentA extends BaseFragment
{
    public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_a, container, false);
        ...
        root.findViewById(R.id.fragment_a_next_button)
            .setOnClickListener( new View.OnClickListener() {
                 public void onClick ( View v ) {
                      baseActivityCallback.addFragment( FragmentB.newInstance(), R.id.main_activity_fragment_container );
                 }
             });
    }
}

public class FragmentB extends BaseFragment
{
    public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_b, container, false);
        ...
        root.findViewById(R.id.fragment_b_pop_silently_button)
            .setOnClickListener( new View.OnClickListener() {
                 public void onClick ( View v ) {
                      baseActivityCallback.popFragment( true );
                 }
             });
    }
}
0

アニメーションなしでポップしたいフラグメントでこれをオーバーライドし、入力してもアニメーションを保持します

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if(!enter){
        Animation a = new Animation() {};
        a.setDuration(0);
        return a;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}
0
Phan Van Linh

別のオーバーロードされたsetCustomAnimation()のメソッドを使用します。このメソッドではR.anim.slide_outを設定せずに問題を解決します

乾杯:)

0
Deepak Goel

より簡単なソリューション:

for (fragment in supportFragmentManager.fragments) {
    removeFragment(fragment)
}
if (supportFragmentManager.backStackEntryCount > 0) {
    supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
0
Javier Antón

これはoverridePendingTransition(int enterAnim, int exitAnim)を両方の0アニメーションなし。

FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
    fm.popBackStack();
    overridePendingTransition(0, 0);
}
0
Markus Rubey

あなたの質問に答える前に、自分で質問する必要があります。

2番目のアクティビティのonBackPressed()メソッドで、最初のアクティビティのバックスタックにアクセスできますか?

はいの場合、popBackStackImmediate(String trnaisiotnName、int inclusive)を呼び出すと、バックスタックからフラグメント遷移が削除され、アニメーションを心配する必要はありません。

私はあなたが前のアクティビティのバックスタックにアクセスできると仮定しています、そうでなければこれは動作しません

0
ufdeveloper