2012-02-08 3 views
43

I spingere un frammento nello stack frammento utilizzando il codice seguente:Pop la backstack frammento senza giocare la Pop-Animation

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

In questo modo, quando la pila frammento viene estratto, ad esempio premendo il pulsante Indietro, viene riprodotta un'animazione pop frammento. Tuttavia, ci sono situazioni in cui vorrei inserire il backstack del frammento senza mostrare questa animazione, ad es. perché sono appena tornato da un'altra attività e desidero visualizzare il frammento precedente in una sola volta, senza animazione.

Un esempio di navigazione potrebbe essere la seguente:

  • L'utente si trova sulla schermata di avvio con il frammento di radice
  • Si seleziona una voce sul frammento radice che quindi visualizza un nuovo frammento di mostrare dettagli di quell'oggetto. Lo fa utilizzando una transazione frammento che imposta le animazioni sia per il push che per il pop (quindi quando l'utente preme il pulsante Indietro, la transizione è animata)
  • Da questo frammento inizia un'attività che (per qualsiasi motivo) elimina l'elemento che è stato appena mostrato
  • Quando questa attività termina, vorrei tornare al frammento di radice senza mostrare il "animazione pop" del "dettaglio frammento"

c'è un modo per far apparire il frammento backstack senza riprodurre l'animazione pop specificata?

+0

Cosa intendi con "Sono appena tornato da un'altra attività?" Puoi dire i passaggi di transizione, ad esempio, come stai cercando di navigare. – 500865

+0

Ciao 500865, ho aggiunto un esempio di navigazione alla domanda. – ChristophK

+0

non imposta 0 come terzo e quarto argomento in setCustomAnimations fai questo? – Boy

risposta

72

Così Warpzit era sulla strada giusta, lui non ha affrontato il problema specifico troppo bene. Mi sono imbattuto nello stesso identico problema ed ecco come l'ho risolto.

Per prima cosa ho creato una variabile booleana statica (per semplicità, permette di metterlo nella classe FragmentUtils) ...

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

Poi, in ogni frammento si dispone, è necessario eseguire l'override del metodo 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); 
} 

Poi, quando è necessario cancellare la backstack dalla vostra attività è sufficiente effettuare le seguenti ...

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

E voilà, una chiamata a clearBackStack() vi porterà indietro nel frammento di radice, senza animazioni di transizione.

Speriamo che la grande G aggiungerà un modo meno stupido di fare questo in futuro.

+6

Questo funziona ma il frammento verrà comunque visualizzato per un breve telaio. C'è un modo per non mostrarlo affatto? – plackemacher

+1

+1 sembra essere l'unica cosa che funziona per me su 2.3 e 4.3 :) – Dori

+7

+1 Si deve tenere a mente che funziona solo con popBackStackImmediate (non funziona con popBackStack). – Tiago

0

Basta utilizzare un altro metodo di overload di setCustomAnimation() e in cui non si imposta la R.anim.slide_out e che risolverà il vostro problema

Saluti :)

+0

Ciò tuttavia impedire qualsiasi animazione di giocare quando l'utente preme il pulsante indietro, non è vero? Voglio l'animazione nel caso di default (l'utente preme il tasto indietro), tuttavia c'è un caso in cui lo stack di frammenti viene scoppiato in risposta a un pulsante push, e in questo caso non voglio un'animazione. – ChristophK

4

L'utente è sulla schermata iniziale con il frammento della radice

Diciamo che il frammento della radice è contenuto nell'attività A.

Seleziona un elemento sul frammento di radice che visualizza un nuovo frammento per mostrare i dettagli di quell'elemento. Lo fa utilizzando una transazione frammento che imposta animazioni sia per la spinta e il caso pop (in modo che quando l'utente preme il pulsante posteriore, la transizione è animato)

La transazione viene aggiunto alla pila avanti. Ciò significa che quando il pulsante Indietro viene premuto dal frammento di dettaglio, il processo di popping è animato.

Da questo frammento inizia un'attività che (per qualsiasi motivo) elimina l'elemento appena mostrato.

permette di dire che è attività B

Quando questa attività termina, vorrei tornare al frammento di radice senza mostrare il "animazione pop" del "dettaglio frammento"

Un modo per ottenere questo comportamento è in questo modo in attività 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(); 

verrà avviata ° e Attività A reimpostarlo allo stato di root in base allo documentation. (controllare l'ultimo paragrafo nella sezione che dice "Questa modalità di avvio può essere utilizzata anche in combinazione con FLAG_ACTIVITY_NEW_TASK: ......")

Con questa configurazione, l'animazione sarà presente in caso di default, mentre nel caso particolare è possibile controllare l'animazione utilizzando:

intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 

che inizia una nuova attività senza animazioni. Se si desidera un'animazione, è possibile farlo utilizzando il metodo overridePendingTransition.

+0

Grazie per la tua risposta! C'è un solo problema per la mia app specifica: L'attività A è contenuta all'interno di un TabHost creato dalla mia "attività principale" (chiamiamola C). C'è un modo per resettare A senza resettare C? – ChristophK

+0

Se si controlla il primo paragrafo nel link che ho postato nella risposta, vedrete "tutte le altre attività su di esso saranno chiuse". Poiché C è sotto A nello stack (l'attività Tabhost sarà in fondo alla pila) non verrà ripristinata C. – 500865

+0

Sfortunatamente, questo non funziona: quando eseguo il codice nell'attività B, l'attività A non viene visualizzata nella sua scheda, invece, viene mostrato "schermo intero" – ChristophK

6

Così, per la libreria di supporto seguenti opere:

Nel frammento che dovrebbe avere un'animazione personalizzata pop si sostituisce l'onCreateAnimation con il proprio personalizzato. Potresti prenderlo e impostare qualche tipo di parametro in base a ciò che desideri. Potrebbe essere necessario fare del lavoro extra per farlo funzionare con frammenti regolari.

Ecco l'esempio in cui sto imperativi e modificare la durata set:

@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 { 

} 
+0

La risposta di Warpzit è in realtà corretta, ma non è spiegata molto bene ... Poiché non posso inserire il codice in un commento, ho intenzione di inviare un'altra risposta ... – Geoff

0

Prima di rispondere alla tua domanda, ho bisogno di fare una domanda io stesso.

Nel metodo onBackPressed() della seconda attività, si può accedere al backstack della prima attività?

In caso affermativo, è possibile chiamare popBackStackImmediate (String trnaisiotnName, int inclusive) e rimuoverà la transizione del frammento dal backstack e non sarà necessario preoccuparsi delle animazioni.

Io parto dal presupposto è possibile accedere backstack dell'attività precedente, altrimenti questo non funzionerà

-1

Rispondi a Geoff e commentare plackemacher.

Si può provare a rimuovere tutte le viste da questo frammento. Quindi il frammento mostrerà ma dovrebbe essere trasparente.

Rimuovere tutti i-1 (io uso cassetto navigare in modo frammento cassetto dovrebbe rimanere) frammento:

int size = fragmentsList.size()-1; 

    FragmentTransaction transaction = fragmentManager.beginTransaction(); 
    transaction.setTransition (FragmentTransaction.TRANSIT_NONE); 

    Fragment fragment; 

    for (int i = size ; i > 0 ; i--) 
    { 
     fragment = fragmentsList.get (i); 
     if(fragment != null) 
     { 
      View viewContainer = fragment.getView(); 
      if (viewContainer != null) 
      { 
       ((ViewGroup) viewContainer).removeAllViews(); 
      } 
      transaction.remove (fragment); 
     } 
    } 

    size = fragmentManager.getBackStackEntryCount(); 

    for (int i = 0; i < size ; i++) 
    { 
     fragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 
    } 

Ci dispiace per il mio inglese

0

Questo è abbastanza facile da raggiungere attraverso overridePendingTransition(int enterAnim, int exitAnim) sia con 0 per nessuna animazione .

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

Questo funziona solo se si lavora con le attività. La domanda si riferisce ai frammenti. – LightMan

1

Quindi, vorrei suggerire una piccola modifica alla risposta di @ Geoff.

Invece di avere un booleano statico globale, preferirei uno locale non statico. Questo è quello che mi è venuto in mente.

creare un'interfaccia

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

Rendere il frammento di implementare tale interfaccia.

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

E poi, quando si desidera disabilitare le animazioni di transizione per un frammento, basta fare

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

per consentire loro, basta fare

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

Se si vuole fare il lo stesso per tutti i frammenti nel gestore dei frammenti, basta fare

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

Molto simile, ma senza campi statici.

0

Questo è il seguito dell'eccellente risposta di @ Geoff, ma adattato per uno scenario più dinamico e reale.

Ho immaginato che fosse un bel post, ma mi rendo conto ora che è diventato un po 'fuori mano. Tuttavia, il codice è tutto lì e lo trovo davvero utile, anche se copre molto più di come disabilitare le animazioni di transizione.

Di solito, quando lavoro con Fragments mi piace avere un BaseFragment che si collega a BaseActivityCallback.Questo BaseActivityCallback può essere utilizzato dai miei Frammenti per aggiungere un nuovo frammento su se stesso, o anche al pop Frammenti sotto di essa, da qui il desiderio di disabilitare le animazioni pop - o pop silenzio:

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

Ora è possibile creare il MainActivity e che hanno mostrano un Fragment1 che può aggiungere un altro Fragment2 che può a sua volta pop Fragment1silenzio:

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

sostituire questa nel frammento che si vuoi fare il pop senza animazione e mantenere l'animazione quando inserisci

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