Scene transition with hero elements throws Layer exceeds max. dimensions supported by the GPU

Christer Nordvik picture Christer Nordvik · Oct 29, 2014 · Viewed 17.1k times · Source

I am trying to do a hero transition in Android 5.0 between an image in a ListView to a details page. But for about 50% of my images Android crashes with the exception below. My images are 600x400 but I tried changing them to 200x100 but got the same error. When it works, it looks great but I cannot see the difference between the various images. Really unsure why it claims the layer is too big, anybody got a clue?

private void handleNewsItemClicked(AdapterView<?> arg0, View viewClicked, int arg2) {
    TopNewsItem item = ((TopNewsItem)arg0.getAdapter().getItem(arg2));
        Intent intent = new Intent(getActivity(), TopNewsDetailsActivity.class);
        intent.putExtra(TopNewsDetailsActivity.ARGS_ID, item.getGuid().getId());
        intent.putExtra(TopNewsDetailsActivity.ARGS_IMAGE,  imageUrl);
        if(Build.VERSION.SDK_INT >= 21) {                                 
            ActivityOptions options = ActivityOptions
                    .makeSceneTransitionAnimation(this.getActivity(),
                            viewClicked.findViewById(R.id.image), "article_image");                
            this.getActivity().startActivity(intent, options.toBundle());
        }else
            getActivity().startActivity(intent);
    }
}

W/OpenGLRenderer(18137): Layer exceeds max. dimensions supported by the GPU (1080x4628, max=4096x4096)
D/AndroidRuntime(18137): Shutting down VM
V/GAV3    (18137): Thread[main,5,main]: Tracking Exception: IllegalStateException (@MessageQueue:nativePollOnce:-2) {main}
/AndroidRuntime(18137): FATAL EXCEPTION: main
E/AndroidRuntime(18137): Process: myapp.app, PID: 18137
E/AndroidRuntime(18137): java.lang.IllegalStateException: Unable to create layer for  RelativeLayout
E/AndroidRuntime(18137):    at android.os.MessageQueue.nativePollOnce(Native Method)
E/AndroidRuntime(18137):    at android.os.MessageQueue.next(MessageQueue.java:143)
E/AndroidRuntime(18137):    at android.os.Looper.loop(Looper.java:122)
E/AndroidRuntime(18137):    at android.app.ActivityThread.main(ActivityThread.java:5221)
E/AndroidRuntime(18137):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(18137):    at java.lang.reflect.Method.invoke(Method.java:372)
E/AndroidRuntime(18137):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
E/AndroidRuntime(18137):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Answer

George Mount picture George Mount · Oct 29, 2014

The Fade transition will use hardware layers when your view does has "hasOverlappingRendering()" return true. This was done for performance. You must have many views all fading out separately.

You have a couple options. One is for your views to have hasOverlappingRendering return false. This may not be possible in all cases, but it may be enough to solve your problem. Remember that this means that the contained views should not overlap!

The second is to transition fewer views separately. You can do this by setting android:transitionGroup="true" on ViewGroups that should be faded out together. For example, if you have a ListView with no background, you'll end up transitioning each element separately. Instead, you can set the ListView's transitionGroup property to true and then they'll transition together.

-- update --

The problem you're encountering is that the incoming Views that are fading in are too large. I tried the following View in the launching Activity:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:onClick="clicked"
    >
    <TextView
        android:id="@+id/hello_world"
        android:transitionName="hello"
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

Yes, the hello world basic app from android, giving an id and transitionName to the text. The onClick handler just launches the second Activity, passing the hello world View as a shared element. The second Activity has an extremely large TextView, similar to yours:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:transitionGroup="true"
    >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/hello_world"
            android:transitionName="hello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="@string/hello_world" />
        <TextView
            android:layout_below="@+id/hello_world"
            android:text="Lorem ipsum.... I didn't copy all of my text"
            android:textSize="30sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
    </RelativeLayout>
</ScrollView>

Switching the ScrollView's transitionGroup from false (the default value) to true makes it work because then the ScrollView is being faded in. The ScrollView has a maximum size, while its contents can be enormous.