38

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

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

所以我在两个最终的活动视图上设置了一个 android:transitionName="item" ,以及回收器视图项目视图。

在进行下一个活动时,我也在使用此代码:

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

单击项目时,它会正确转换并显示新视图。这真的很好。但是,当我单击后退按钮时。有时它工作正常,但大多数时候我的活动因以下堆栈跟踪而崩溃:

   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中的一个错误

4

12 回答 12

8

我遇到了同样的问题,并注意到如果返回时原始共享元素在前一个屏幕上不再可见,则会发生崩溃(可能它是纵向屏幕上的最后一个元素,但一旦切换到横向,它就不再可见) ,因此过渡没有地方放回共享元素。

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

@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();
    }
}
于 2015-04-06T06:15:44.190 回答
4

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

-keep public class android.app.ActivityTransitionCoordinator

于 2014-11-13T17:31:18.687 回答
4

尝试删除您在最终活动视图中可能拥有的任何合并 xml 标记。我注意到转换到包含合并标签的视图,其中转换元素是合并标签的直接子元素,会导致此错误,但我应该用不同的容器(如 CardView)替换合并标签,动画工作得很好。还要确保视图中的 transitionNames 之间存在 1:1 的关系。

更新:我在进行活动转换时再次遇到此问题,单击后退按钮返回到初始活动,然后再次尝试转换。我通过 id 访问“过渡组件”的直接父级(A RelativeLayout),调用 findViewById(),然后调用 removeAllViews()。我最终将代码更改为在比父级更大的祖先上调用“removeAllViews()”,还从元素中删除了一个标签,该标签将在页面加载后取代“过渡组件”。这缓解了我的问题。

于 2015-03-13T18:02:55.703 回答
3

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

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

作为最后的措施,虽然我不确定这是否有所作为,但我也在recyclerView.setLayoutFrozen(true)/recyclerView.setLayoutFrozen(false)中做了onTransitionStart/ onTransitionEnd

于 2017-02-28T10:46:18.323 回答
3

确保您在第二个活动中转换到的视图不是根布局。您可以将其包装在带有透明 windowBackground 的 FrameLayout 中。

于 2016-11-24T15:33:40.697 回答
2

确保您在转换中传递的“itemView”是单击的视图(在您的 onClick() 回调中收到)

于 2015-02-26T13:48:57.140 回答
2

我遇到了同样的问题,实际上我使用了 firebase 并且我有信息列表,当用户点击它时,它会在这个活动中使用 sharedAnimation 调用 detailActivity 我正在使用 firebase 更新它,所以 firebase 事件会更新列表项,在这个如果这个问题被调用,因为回收者认为屏幕布局已经生效。

它调用了一个异常,因为我们传递给它的那个转换 id 已经不存在了,所以我使用这个方法解决了这个问题。

onPause() 我已经冻结了布局并且 onResume() 将其设置为 false;

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

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

它正在工作。

于 2017-07-10T12:28:39.130 回答
1

我遇到了同样的错误,我的错误是由 hidro 的答案背后的相同原因引起的,但是是由键盘隐藏了过渡要返回的共享元素引起的。

我的解决方法是在完成活动之前以编程方式关闭键盘,这样前一个活动的共享元素就不会被遮挡。

View view = this.getCurrentFocus();
if (view != null) {  
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
supportFinishAfterTransition();
于 2016-12-21T02:33:37.517 回答
1

正如@Fabio Rocha所说,确保itemViewViewHolder.

您可以ViewHolder通过

mRecyclerView.findViewHolderForAdapterPosition(position);
于 2017-03-02T15:30:49.813 回答
1

我想出的是避免使用 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());
    }
}
于 2016-12-02T13:48:36.317 回答
0

遇到了同样的问题,它是由后台更新回收器视图引起的,当 notifyItemChanged(int index) 时回收器视图将重新创建视图,因此共享视图被回收并在返回时崩溃。

我的解决方案是 call recyclerView.setItemAnimator(null);,它会阻止 recycler view 重新创建视图。

于 2018-10-31T06:35:10.343 回答
0

原因其实很简单:当你导航回父 Activity 或 Fragment 时,View 还没有出现(可能有很多原因)。
因此,您要做的是将 Enter 转换推迟到 View 可用为止。

我的解决方法是在我的 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;
        }
    });
}
于 2017-05-22T22:15:38.197 回答