Android Shared Element Transition with Rotation, possibly broken?
Asked Answered
T

0

8

(See updates after code.)

I have a screen with a rotated View that I want to transition, ideally using the built-in shared element transition feature of Android, to another screen. However, the animation that results when the rotation of the View is set looks broken, even if the rotations are the same. I looked at the various transitions effects for shared element transition and the "ChangeTransform" specifically mentions rotation (and looking at the code confirms that). Using it, or not using it, has the same results. (That is to say, setting it explicitly or just using the defaults has no change in results of the look of the animation, which I'll describe below.)

So what happens? Let me explain by way of example. Let's take two Activities -- we'll get rid of all Fragments so we know it's not the delayed transition getting in the way -- call them MainActivity and AnotherActivity. MainActivity launches AnotherActivity with shared element transitions active. Take two Views, viewOnene and viewTwo. ViewOne has no rotation. ViewTwo has a rotation set in MainActivity but not AnotherActivity. In MainActivity both views are on the left and in AnotherActivity both views are on the right. They have different sizes.

Here's what happens, when going forward:

  • ViewOne animates over to it's target location, and scales in size appropriately. This looks fine and is expected.
  • ViewTwo pops to no rotation (e.g. instant rotation to 0), then animates over to the target location and size. Looks horrible.

Here's what happens when going backward:

  • ViewOne reverses the animation, and all looks fine.
  • ViewTwo animates back to the target location, and is a little too big. Then it pops to a slightly different location and size (bigger, oddly enough) and the destination rotation and translates and scales into place. This looks horrible.

You probably need to see it for yourself. Here: https://youtu.be/cUlVZgmJV_A

I have tried changing the transitions via code directly, view TransitionSet and reference from the style, and the results have been the same. It's like the transition gets split in half when there's a rotation (whether from code or XML).

I created a small project to demonstrate, and can share the entire project if needed. However, I suspect I might just be missing something simple, especially since the one view does work fine for the transition.

What I would expect to happen is the rotation to change smoothly with the translation and scale. This is relatively straightforward with an animator, though I'd like to avoid manually created shared element transitions, if at all possible.

Here are the relevant parts of the relevant files:

MainActivity.java

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final View viewOne = findViewById(R.id.viewOne);
        final View viewTwo = findViewById(R.id.viewTwo);

        viewOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, AnotherActivity.class);
                Pair<View, String> viewOneTx = new Pair<>(viewOne, "view_one");
                Pair<View, String> viewTwoTx = new Pair<>(viewTwo, "view_two");

                ArrayList<Pair<View, String>> viewsList = new ArrayList<>();

                viewsList.add(viewOneTx);
                viewsList.add(viewTwoTx);

                ActivityOptionsCompat options;

                //noinspection unchecked
                Pair<View, String>[] sharedElements = (Pair<View, String>[]) new Pair[viewsList.size()];
                viewsList.toArray(sharedElements);
                //noinspection unchecked
                options = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, sharedElements);


                ActivityCompat.startActivity(MainActivity.this, intent, options.toBundle());
            }
        });
    }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:clipChildren="false"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.test.transitionplay.MainActivity">


    <View
        android:id="@+id/viewOne"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#f0f"
        android:transitionName="view_one" />


    <View
        android:id="@+id/viewTwo"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:background="#f00"
        android:rotation="-45"
        android:layout_alignParentBottom="true"
        android:transitionName="view_two" />
</RelativeLayout>

activity_another.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.test.transitionplay.AnotherActivity">

    <View
        android:id="@+id/anotherViewOne"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_alignParentRight="true"
        android:background="#f0f"
        android:transitionName="view_one" />

    <View
        android:id="@+id/anotherViewTwo"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:background="#f00"
        android:transitionName="view_two" />

</RelativeLayout>

Update #1

I have narrowed down the issue to ChangeTransform, I believe. Around line 282, there's a comment, "clear the transform properties so that we can use the animation matrix instead" and the code, quite literally, sets all the regular transform properties (like rotation and translation) to zero on the view. That could explain why the UI sees this behavior...

Update #2

I decided to try to make a simple transform for just the rotation, since the actual movement (as handled by ChangeBounds) appears to be working. When I did so, the transform was receiving start and end views that were not only identical, but also had a rotation of zero (the view on the second activity -- I set a tag to confirm). The result? Same problem as ChangeTransform... so maybe the issue is at a higher level?

That said, I'm having a hard time believing that it simply doesn't work... I must just be missing something.

Update #3

In the video, the "bounce" at the end, most notable on the return, doesn't seem to be coming from the shared element transitions at all. I've been working on a work-around in the meantime, and that part is still there, even though I have the other parts mostly working. That gives more evidence that I might be missing something important -- or that it's broken.

Talya answered 9/2, 2016 at 16:50 Comment(4)
I have to say, it does seem to just not work. I'm keeping an eye on this.Yama
look like an OS level issue. Do you have any updates on this?Mastaba
Did you manage to solve this OP?Myrilla
Has anyone found a solution for this?Sclaff

© 2022 - 2024 — McMap. All rights reserved.