studio programacion herramientas fundamentos con avanzado aplicaciones android android-animation android-recyclerview shared-element-transition activity-transition

programacion - Transición de actividad de elementos compartidos en android 5



manual de android en pdf (11)

Quería configurar una transición de elementos compartidos cuando iba de una Actividad a otra.

La primera actividad tiene un RecyclerView con elementos. Cuando se hace clic en un elemento, este debe animarse a la nueva actividad.

Así que establecí un android: transitionName = "item" en las vistas de actividad final, así como en las vistas de elementos de recycler-view.

También estoy usando este código cuando voy a la siguiente actividad:

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

Al hacer clic en un elemento, se transiciona correctamente y se muestra la nueva vista. Es muy agradable. Sin embargo, cuando hago clic en el botón Atrás. A veces funciona bien, pero la mayoría de las veces mi actividad falla con la siguiente 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)

¿Qué estoy haciendo mal? Parece un error en Android 5


Asegúrese de que la "vista de elemento" que está pasando en la transición se haga clic en la vista (se reciba en su devolución de llamada onClick ())


Asegúrese de que la Vista a la que está realizando la transición en la Segunda actividad no es el diseño de la raíz. Puedes envolverlo en FrameLayout con un windowBackground transparente.


Como dijo @Fabio Rocha , asegúrese de que itemView se recupere del ViewHolder .

Puede obtener ViewHolder por posición a través de

mRecyclerView.findViewHolderForAdapterPosition(position);


He enfrentado el mismo problema, en realidad utilicé Firebase y tengo una lista de información y cuando el usuario toque se llamará detailActivity con sharedAnimation en esta actividad que estaba actualizando como se vio usando firebase para que firebase evento actualizando el elemento de la lista como se ve, en este caso de que este problema esté invocando porque el reciclador ve que el diseño de la pantalla estaba siendo afectado.

e invoca una excepción porque esa transición id a la que hemos pasado ya no existía, entonces resuelvo este problema usando este método.

onPause () He congelado el diseño y onResume () lo configuró como falso;

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

Y está funcionando.


Intente eliminar las etiquetas xml de fusión que pueda tener en la vista de la actividad final. Me he dado cuenta de que la transición a una vista, que contiene una etiqueta de combinación, en la que el elemento de transición es un elemento directo de la etiqueta de combinación, causará este error, pero si reemplazo la etiqueta de fusión con un contenedor diferente como CardView, la animación funciona bien También asegúrese de que exista una relación 1: 1 entre transitionNames en las vistas.

ACTUALIZACIÓN: Experimenté este problema una vez más al hacer una transición de actividad, al hacer clic en el botón Atrás para volver a la actividad inicial y luego volver a intentar la transición. Estaba accediendo al padre directo del ''componente de transición'', ( RelativeLayout ) por id, con una llamada findViewById () y luego llamando a removeAllViews (). Terminé cambiando el código para llamar a ''removeAllViews ()'' en un ancestro mayor que el principal, también eliminé una etiqueta del elemento que tomaría el lugar del ''componente de transición'' después de cargar la página. Esto alivia mi problema.


La razón de esto es bastante simple: cuando navega de regreso a la Actividad o Fragmento principal, la Vista aún no está allí (podría ser por muchas razones).
Por lo tanto, lo que desea hacer es posponer Enter Transition hasta que la vista esté disponible.

Mi trabajo es llamar a la siguiente función en onCreate () en mi Fragmento (pero también funciona en la Actividad):

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


Lo que se me ocurrió es evitar la transición a la Actividad con RecyclerView, o cambiar la transición hacia atrás con otra cosa.

Deshabilitar todas las transiciones de retorno:

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

O bien, si desea deshabilitar solo los elementos compartidos, devuelva la transición y pueda establecer su propia transición de retorno:

// 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 eso implementado en la actividad base, puede usarlo fácilmente en 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()); } }


Me encuentro con el mismo problema y noto que el bloqueo ocurre si el elemento compartido original ya no está visible en la pantalla anterior cuando retrocedes (probablemente sea el último elemento en la pantalla en retrato, pero una vez que se haya cambiado al paisaje ya no está visible) y, por lo tanto, la transición no tiene lugar para volver a colocar el elemento compartido.

Mi solución es eliminar la transición de retorno (en la segunda actividad) si la pantalla se ha girado antes de volver, pero estoy seguro de que debe haber una mejor manera de manejar esto:

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


Si está usando Proguard, intente agregar esto en su archivo de reglas. Tuve el mismo problema y parece que funciona?

-keep public class android.app.ActivityTransitionCoordinator


Tuve el mismo error, el mío fue causado por el mismo razonamiento detrás de la respuesta de hidro, pero fue causado por el teclado que ocultaba el elemento compartido al que estaba regresando la transición.

Mi solución fue cerrar el teclado programáticamente justo antes de finalizar la actividad para que el elemento compartido en la actividad anterior no se oscurezca.

View view = this.getCurrentFocus(); if (view != null) { InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } supportFinishAfterTransition();


Tuve este mismo problema, para mí fue causado por la visión de reciclaje que ejecuta actualizaciones después / durante la primera transición de salida. Creo que la vista de elementos compartidos a veces se reciclaba, lo que significa que ya no estaría disponible para la animación de transición, de ahí el bloqueo (normalmente en la transición de retorno pero a veces en la transición de salida). Lo resolví bloqueando las actualizaciones si la actividad se pausó (usé un indicador isRunning) - nótese que se estaba pausando pero no deteniéndose, ya que aún estaba visible en el fondo. Además, bloqueé el proceso de actualización si la transición se estaba ejecutando. Me pareció suficiente para escuchar esta devolución de llamada:

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

Como medida final, aunque no estoy seguro de si esto marcó la diferencia, también hice recyclerView.setLayoutFrozen(true) / recyclerView.setLayoutFrozen(false) en onTransitionStart / onTransitionEnd .