在android 5上共享元素活动转换

从一个Activity到另一个Activity时,我想设置共享元素转换

第一个活动有一个RecyclerView与项目。 当一个项目被点击时,该项目应该animation到新的活动。

所以我在最终的活动视图和回收视图项目视图中都设置了android:transitionName =“item”。

进入下一个活动时,我也使用这个代码:

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

点击一个项目时,它会正确转换并显示新的视图。 这真的很好。 但是,当我点击后退button。 有时它可以正常工作,但大部分时间我的活动崩溃与以下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) 

我究竟做错了什么? 它看起来像在Android 5中的错误

Solutions Collecting From Web of "在android 5上共享元素活动转换"

我遇到了同样的问题,并且注意到如果原始共享元素在前一个屏幕上不再显示时(可能是纵向屏幕上的最后一个元素,但切换到横向时不再可见),会发生崩溃。 ,这样的过渡无处可以把共同的要素放回去。

我的解决方法是删除返回转换(在第二个活动),如果屏幕已经旋转之前,回来,但我相信必须有一个更好的办法来处理这个问题:

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

尝试移除您在最终活动视图中可能具有的任何合并xml标记。 我注意到,过渡到一个视图,其中包含一个合并标记,其中转换元素是合并标记的直接孩子,将导致此错误,但是我应该replace合并标记与CardView不同的容器,animation工作得很好。 还要确保视图中transitionNames之间有1:1的关系。

更新:在做活动转换时,我再次遇到此问题,单击后退button返回到初始活动,然后再次尝试转换。 我正在通过调用findViewById()方法访问“转换组件”的直接父项( RelativeLayout ),然后调用removeAllViews()。 我最终改变了代码,在比父代祖先更大的祖先上调用“removeAllViews()”,也从页面加载后代替“转换组件”的元素中删除了一个标签。 这缓解了我的问题。

如果您正在使用Proguard,请尝试将其添加到您的规则文件中。 我有同样的问题,似乎工作?

-keep public class android.app.ActivityTransitionCoordinator

确保您在第二个活动中转换的视图不是根布局。 你可以用一个透明的windowBackground把它包装在一个FrameLayout中。

确保在转换中传递的“itemView”是单击的视图(onClick()callback)

我有这个相同的问题,对我来说,这是由recyclerview在第一次退出之后/期间执行更新引起的。 我认为共享元素视图有时被回收,这意味着它不再可用于过渡animation,因此崩溃(通常在返回转换,但有时在退出转换)。 我解决了阻止更新,如果活动暂停(使用isRunning标志) – 注意它暂停,但不停止,因为它仍然在后台可见。 此外,我阻止了更新过程,如果转换正在运行。 我发现它足够听这个callback:

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

作为最后的措施,虽然我不知道这是否onTransitionStart ,我还在onTransitionStart / onTransitionEnd做了recyclerView.setLayoutFrozen(true) / recyclerView.setLayoutFrozen(false)

我想到的是避免使用RecyclerView过渡回Activity,或者改变其他的东西。

禁用所有返回转换:

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

或者,如果要禁用仅共享元素的返回转换,并能够设置自己的返回转换:

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

在基本活动中实现的,你可以很容易地在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()); } } 

我有这个相同的错误,我的是由hidro的答案背后的同样的理由引起的,但是由键盘隐藏共享元素,转换是回到。

我的解决方法是在完成活动之前以编程方式closures键盘,以便前面活动的共享元素不被遮挡。

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

正如@Fabio Rocha所说的,确保ViewHolder是从ViewHolder获取的。

您可以通过位置获取ViewHolder

 mRecyclerView.findViewHolderForAdapterPosition(position); 

其原因其实很简单:当你回到父Activity或Fragment时, View还没有 (可能是因为很多原因)。
所以,你想要做的是推迟input转换,直到视图可用。

我的解决方法是在我的Fragment中调用onCreate()中的以下函数(但也适用于Activity):

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

我面临同样的问题,实际上我用firebase和我有信息列表,当用户点击它将调用与此活动sharedAnimation detailActivity我正在更新它看到使用firebase所以firebase事件更新列表项目所见,在这这个问题正在调用,因为回收商查看屏幕布局正在实施。

它会调用一个exception,因为我们已经通过它的那个转换标识已经不存在了,所以我使用这个方法解决了这个问题。

onPause()我冻结了布局和onResume()将其设置为false;

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

它正在工作。