Lollipop 设备的共享元素转换崩溃

Posted

技术标签:

【中文标题】Lollipop 设备的共享元素转换崩溃【英文标题】:Shared Element Transition Crash for Lollipop Devices 【发布时间】:2017-02-23 16:30:03 【问题描述】:

我正在使用ActivityOptions.makeSceneTransitionAnimation API 来处理共享转换。

我面临的问题是它可以在 Marshmellow 设备上运行,但在 Lolipop 设备上崩溃。

这是崩溃日志:

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.ViewGroup.getScrollX()' on a null object reference
   at android.app.ActivityTransitionCoordinator.setSharedElementState(ActivityTransitionCoordinator.java:435)
   at android.app.ActivityTransitionCoordinator.setSharedElementState(ActivityTransitionCoordinator.java:473)
   at android.app.EnterTransitionCoordinator.startSharedElementTransition(EnterTransitionCoordinator.java:332)
   at android.app.EnterTransitionCoordinator.access$200(EnterTransitionCoordinator.java:42)
   at android.app.EnterTransitionCoordinator$5$1.run(EnterTransitionCoordinator.java:389)
   at android.app.ActivityTransitionCoordinator.startTransition(ActivityTransitionCoordinator.java:698)
   at android.app.EnterTransitionCoordinator$5.onPreDraw(EnterTransitionCoordinator.java:386)
   at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847)
   at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1990)
   at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1088)
   at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5825)
   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:769)
   at android.view.Choreographer.doCallbacks(Choreographer.java:582)
   at android.view.Choreographer.doFrame(Choreographer.java:552)
   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:755)
   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:5237)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:707)

这是我的代码

 @Override
public void getComments(String postId, String authorName, String message, String imageURL, String likesCount, RelativeLayout imageView) 


    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && imageView != null) 
        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(getActivity(), imageView, "revealLayout");

        Intent commentIntent = new Intent(getActivity(), CommentsActivity.class);
        commentIntent.putExtra("postId", postId);
        commentIntent.putExtra("authorName", authorName);
        commentIntent.putExtra("message", message);
        commentIntent.putExtra("imageURL", imageURL);
        commentIntent.putExtra("likesCount", likesCount);
        startActivity(commentIntent, options.toBundle());

     else 
        Intent commentIntent = new Intent(getActivity(), CommentsActivity.class);
        commentIntent.putExtra("postId", postId);
        commentIntent.putExtra("authorName", authorName);
        commentIntent.putExtra("message", message);
        commentIntent.putExtra("imageURL", imageURL);
        commentIntent.putExtra("likesCount", likesCount);
        startActivity(commentIntent);
    



我通过接口从适配器回调中获取上述数据

我的 XML :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/root_layout"
  android:layout_
  android:transitionName="revealLayout"
  android:layout_
  android:background="?android:attr/selectableItemBackground"
  android:orientation="vertical">

与接收端android:transitionName相同的相对布局

这是我的styles.xml

  <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">#c65643</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowContentTransitions">true</item>
    <item name="android:windowSharedElementReenterTransition">@null</item>
    <item name="android:windowReenterTransition">@null</item>
    <item name="android:windowAllowReturnTransitionOverlap">false</item>
</style>

还有styles.xml (21)

  <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">#c65643</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:windowContentTransitions">true</item>
            <item name="android:windowSharedElementReenterTransition">@null</item>
            <item name="android:windowReenterTransition">@null</item>
            <item name="android:windowAllowReturnTransitionOverlap">false</item>

    </style>

也通过此链接https://code.google.com/p/android/issues/detail?id=224270&sort=-id&colspec=ID 但没有运气

提前致谢

【问题讨论】:

试试看这里链接github.com/teocci/AndroidGuidenotes/wiki/… 在转换完成之前从父级移除视图时出现同样的错误。 【参考方案1】:

显然这是 5.0 中的 Android 错误,并在 5.1 中修复。 尝试将除ScaleType.MATRIX以外的其他ScaleType设置为Transition ImageView

Android 5.0 上的ActivityTransitionCoordinator.java 源代码

537    protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) 
538      ...
549        for (String name: names) 
550            Bundle sharedElementBundle = state.getBundle(name);
551            if (sharedElementBundle != null) 
552               ...
557                if (snapshot != null) 
558                    setSharedElementState(snapshot, name, state, null, null, decorLoc);
559                
560                snapshots.add(snapshot);
561            
562        
563        return snapshots;
564    

Android 5.1 上的ActivityTransitionCoordinator.java 源代码

629    protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) 
630       ...
641        Matrix tempMatrix = new Matrix();
642        for (String name: names) 
643            Bundle sharedElementBundle = state.getBundle(name);
644            View snapshot = null;
645            if (sharedElementBundle != null) 
646              ...
650                if (snapshot != null) 
651                    setSharedElementState(snapshot, name, state, tempMatrix, null, decorLoc);
652                
653            
655            snapshots.add(snapshot);
656        
657        return snapshots;
658    

还有 ActivityTransitionCoordinator.setSharedElementState 中的崩溃线

376    private void setSharedElementState(View view, String name, Bundle transitionArgs,
377            Matrix tempMatrix, RectF tempRect, int[] decorLoc) 
378        Bundle sharedElementBundle = transitionArgs.getBundle(name);
379        if (sharedElementBundle == null) 
380            return;
381        
382
383        if (view instanceof ImageView) 
384            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
385            if (scaleTypeInt >= 0) 
386                ImageView imageView = (ImageView) view;
387                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
388                imageView.setScaleType(scaleType);
389                if (scaleType == ImageView.ScaleType.MATRIX) 
390                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
391                    tempMatrix.setValues(matrixValues);
392                    imageView.setImageMatrix(tempMatrix);
393                
394            
395        
396        ...
450    

【讨论】:

以上是关于Lollipop 设备的共享元素转换崩溃的主要内容,如果未能解决你的问题,请参考以下文章

在Pre-Lollipop设备中使用.xml向量时,应用程序崩溃Xamarin.Android

Python使用matplotlib可视化排序的棒棒糖图类似于排序的条形图(Lollipop Chart)

即使在 Lollipop 之前,如何获取当前设备的用户列表?

Android棒棒糖共享元素过渡闪烁/闪烁

使用Material Design在Android Lollipop中的ListView中的活动过渡动画

片段之间的共享元素转换