32

Volevo impostare una transizione di elemento condiviso quando si passa da un'attività all'altra.Transizione attività elemento condivisa su Android 5

La prima attività ha una vista Recycler con elementi. Quando si fa clic su un elemento, quell'elemento dovrebbe animarsi alla nuova attività.

Quindi ho impostato un android: transitionName = "elemento" su entrambe le visualizzazioni di attività finali, nonché wel come le visualizzazioni di elementi di visualizzazione del riciclatore.

Sto anche utilizzando questo codice quando si va alla prossima attività:

this.startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, itemView, "boomrang_item").toBundle()); 

Cliccando un elemento, transizioni correttamente e viene visualizzata la nuova vista. È veramente carino. Tuttavia, quando faccio clic sul pulsante Indietro. A volte funziona bene, ma la maggior parte del tempo i miei arresti di attività con la seguente stacktrace:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.ViewGroup.transformMatrixToGlobal(android.graphics.Matrix)' on a null object reference 
      at android.view.GhostView.calculateMatrix(GhostView.java:95) 
      at android.app.ActivityTransitionCoordinator$GhostViewListeners.onPreDraw(ActivityTransitionCoordinator.java:845) 
      at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847) 
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1956) 
      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054) 
      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779) 
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) 
      at android.view.Choreographer.doCallbacks(Choreographer.java:580) 
      at android.view.Choreographer.doFrame(Choreographer.java:550) 
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753) 
      at android.os.Handler.handleCallback(Handler.java:739) 
      at android.os.Handler.dispatchMessage(Handler.java:95) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5221) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 

Che cosa sto facendo di sbagliato? Sembra un errore in Android 5

+0

potresti aggiungere tutto il codice rilevante in modo che sia più facile capire dove stai andando male? – gaara87

+0

Prova a impostare 'transitionName' su _only_ le viste degli elementi condivisi. –

+0

Ho lo stesso problema quando apro una nuova attività, ruota la schermata e premo il pulsante Indietro. Qualche soluzione? – jclova

risposta

3

Se si utilizza Proguard, provare ad aggiungerlo nel file delle regole. Ho avuto lo stesso problema e sembra funzionare?

-keep public class android.app.ActivityTransitionCoordinator

2

Assicurarsi che il "itemView" si passa nella fase di transizione è la vista fatto clic (ricevuta sul onClick() di callback)

4

provare a rimuovere tutti i tag merge XML che si potrebbe avere sul vista finale dell'attività. Ho notato che la transizione a una vista, che contiene un tag di unione, in cui l'elemento di transizione è figlio diretto del tag di unione, causerà questo errore, ma dovrei sostituire il tag di unione con un contenitore diverso come CardView, l'animazione funziona bene Assicurarsi inoltre che esista una relazione 1: 1 tra i nomi di transizione nelle viste.

UPDATE: Ho riscontrato ancora una volta questo problema durante una transizione di attività, facendo clic sul pulsante Indietro per tornare all'attività iniziale e quindi riprovare la transizione. Stavo accedendo al genitore diretto del "componente di transizione", (A RelativeLayout) da id, con una chiamata findViewById(), quindi chiamando removeAllViews(). Ho finito per modificare il codice per chiamare "removeAllViews()" su un antenato più grande rispetto al genitore, inoltre ho rimosso un tag dall'elemento che avrebbe dovuto prendere il posto del "componente di transizione" dopo il caricamento della pagina. Questo alleviava il mio problema.

7

Ho riscontrato lo stesso problema e ho notato che l'arresto si verifica se l'elemento condiviso originale non è più visibile nella schermata precedente quando si torna indietro (probabilmente è l'ultimo elemento sullo schermo in verticale, ma una volta passato in orizzontale è non è più visibile), e quindi la transizione non ha un posto dove rimettere l'elemento condiviso.

La mia soluzione è rimuovere il passaggio di ritorno (nel 2 ° attività) se lo schermo è stato ruotato prima di tornare, ma sono sicuro che ci deve essere un modo migliore per gestire questa situazione:

@Override 
public void onConfigurationChanged(Configuration newConfig) { 
    super.onConfigurationChanged(newConfig); 
    mOrientationChanged = !mOrientationChanged; 
} 

@Override 
public void supportFinishAfterTransition() { 
    if (mOrientationChanged) { 
     /** 
     * if orientation changed, finishing activity with shared element 
     * transition may cause NPE if the original element is not visible in the returned 
     * activity due to new orientation, we just finish without transition here 
     */ 
     finish(); 
    } else { 
     super.supportFinishAfterTransition(); 
    } 
} 
3

Assicurati che la vista su cui stai effettuando la transizione nella seconda attività non sia il layout di root. Puoi avvolgerlo in un FrameLayout con una finestra trasparente.

+0

Era una soluzione! –

1

Quello che mi è venuto in mente è di evitare il passaggio ad Activity con RecyclerView, o di cambiare transizione con qualcos'altro.

Disattiva tutte le transizioni di ritorno:

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
@Override 
public void finishAfterTransition() { 
    finish(); 
} 

Oppure, se si desidera disattivare gli elementi solo condivise ritorno di transizione, ed essere in grado di impostare il proprio passaggio di ritorno:

// Track if finishAfterTransition() was called 
private boolean mFinishingAfterTransition; 

@Override 
protected void onCreate(@Nullable final Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    mFinishingAfterTransition = false; 
} 

public boolean isFinishingAfterTransition() { 
    return mFinishingAfterTransition; 
} 

@Override 
@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
public void finishAfterTransition() { 
    mFinishingAfterTransition = true; 
    super.finishAfterTransition(); 
} 

public void clearSharedElementsOnReturn() { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     TransitionUtilsLollipop.clearSharedElementsOnReturn(this); 
    } 
} 

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
private static final class TransitionUtilsLollipop { 

    private TransitionUtilsLollipop() { 
     throw new UnsupportedOperationException(); 
    } 

    static void clearSharedElementsOnReturn(@NonNull final BaseActivity activity) { 
     activity.setEnterSharedElementCallback(new SharedElementCallback() { 

      @Override 
      public void onMapSharedElements(final List<String> names, 
        final Map<String, View> sharedElements) { 
       super.onMapSharedElements(names, sharedElements); 
       if (activity.isFinishingAfterTransition()) { 
        names.clear(); 
        sharedElements.clear(); 
       } 
      } 
     }); 
    } 

Con quello implementato in attività di base, si può facilmente utilizzare in onCreate()

@Override 
protected void onCreate(@Nullable final Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    clearSharedElementsOnReturn(this); 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     // set your own transition 
     getWindow().setReturnTransition(new VerticalGateTransition()); 
    } 
} 
1

Ho avuto questo stesso errore, il mio era causato dallo stesso ragionamento dietro la risposta di hidro, ma era causato dalla tastiera che nascondeva l'elemento condiviso a cui la transizione stava tornando.

La soluzione alternativa era chiudere a livello di codice la tastiera prima di terminare l'attività in modo che l'elemento condiviso sull'attività precedente non sia oscurato.

2

Ho avuto lo stesso problema, per me è stato causato dal recyclerview che esegue gli aggiornamenti dopo/durante la prima transizione di uscita. Penso che la vista degli elementi condivisi sia stata talvolta riciclata, il che significa che non sarebbe più disponibile per l'animazione di transizione, da cui il crash (di solito sulla transizione di ritorno ma a volte sulla transizione di uscita). Ho risolto il problema bloccando gli aggiornamenti se l'attività è stata messa in pausa (usato un flag isRunning) - nota che era in pausa ma non si fermava in quanto era ancora visibile in background. Inoltre ho bloccato il processo di aggiornamento se la transizione era in esecuzione. Ho trovato abbastanza per ascoltare questa callback:

Transition sharedElementExitTransition = getWindow().getSharedElementExitTransition(); 
     if (sharedElementExitTransition != null) { 
      sharedElementExitTransition.addListener(.....); 
     } 

Come misura finale, anche se non sono sicuro se questo ha fatto la differenza, ho anche fatto recyclerView.setLayoutFrozen(true)/recyclerView.setLayoutFrozen(false) nel onTransitionStart/onTransitionEnd.

1

Come indicato in @Fabio Rocha, assicurarsi che lo itemView venga recuperato da ViewHolder.

È possibile ottenere il ViewHolder dalla posizione tramite

mRecyclerView.findViewHolderForAdapterPosition(position); 
0

La ragione di questo è in realtà abbastanza semplice: Quando si passa di nuovo alla attività genitore o frammento, il vista non c'è ancora (potrebbe essere per molte ragioni).
Quindi, ciò che si desidera fare è posticipare la transizione di invio fino a quando la vista è disponibile.

mio lavoro intorno è quello di chiamare la seguente funzione nel onCreate() nel mio frammento (ma lavora in attività troppo):

private void checkBeforeTransition() { 
    // Postpone the transition until the window's decor view has 
    // finished its layout. 
    getActivity().supportPostponeEnterTransition(); 

    final View decor = getActivity().getWindow().getDecorView(); 
    decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 
     @Override 
     public boolean onPreDraw() { 
      decor.getViewTreeObserver().removeOnPreDrawListener(this); 
      getActivity().supportStartPostponedEnterTransition(); 
      return true; 
     } 
    }); 
} 
0

ho affrontato lo stesso problema, in realtà ho usato Firebase e Ho un elenco di informazioni e quando l'utente lo tocca chiamerà detailActivity con sharedAnimation in questa attività lo stavo aggiornando come visto utilizzando firebase in modo che l'evento firebase aggiornasse l'elemento list visto, in questo caso questo problema sta invocando perché recycler visualizza quel layout dello schermo veniva eseguito.

e richiama un'eccezione perché l'id di transizione che abbiamo passato non era più, quindi risolvo questo problema utilizzando questo metodo.

onPause() Ho bloccato il layout e onResume() lo ha impostato come falso;

@Override 
public void onPause() { 
    super.onPause(); 
    mRecycler.setLayoutFrozen(true); 
} 

@Override 
public void onResume() { 
    super.onResume(); 
    mRecycler.setLayoutFrozen(false); 
} 

E sta funzionando.

Problemi correlati