Animation between Activities and shared views: glitchy/hack at the ends of animation?

6.9k views Asked by At

So, the problem I am facing is that the animation I do between two Activities and two shared views is not looking great.

The problem is that its "glitchy", when going from Activity2 back to Activity1, the TextViews being shared sort of blinks at the end of the animation, revealing the "bigger text" from Activity2 for a fraction of a second, so it "blinks".

Activity 1 (RecyclerView with three items):

enter image description here

Activity 2 (Details):

enter image description here

I filmed the screen, while doing the animation. When going back from Activity2 to Activit2, you can see the text blink at the very end. This video (36MB, sorry for size) shows it:

https://drive.google.com/file/d/0B3wIZ9CS9Kj_a0MyVFlzX1YtY0E/view?usp=sharing

The question is: what I am doing wrong? Why is it blinking like that? I see videos on other anims, and they are all super smooth and nice.

I have tested different type of transitions (changeBounds, explode, etc etc), but there is always something weird going on. Any tips, ideas would be greatly appreciated =)

MY CODE

MainActivity (Activity1):

package se.snapcode.lollipoptest;

import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.util.Pair;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v7.widget.RecyclerView;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends Activity {

    private RecyclerView mRecyclerView;
    private MyAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    GestureDetectorCompat gestureDetector;

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

        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        // specify an adapter (see also next example)
        String[] strings = new String[3];
        strings[0] = "A1";
        strings[1] = "A2";
        strings[2] = "A3";
        mAdapter = new MyAdapter(strings);
        mRecyclerView.setAdapter(mAdapter);

        mAdapter.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                final TextView headerView = (TextView)view.findViewById(R.id.textView1);
                final TextView textView = (TextView)view.findViewById(R.id.textView2);
                Intent intent = new Intent(MainActivity.this, DetailsActivity.class);
                intent.putExtra("header", headerView.getText().toString());
                intent.putExtra("text", textView.getText().toString());

                ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, Pair.create((View)headerView, "header"),
                        Pair.create((View)textView, "text"));

                startActivity(intent, options.toBundle());
            }
        });

        RecyclerView.ItemDecoration itemDecoration =
                new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);
        mRecyclerView.addItemDecoration(itemDecoration);

        // this is the default; this call is actually only necessary with custom ItemAnimators
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    }
}

DetailsActivity (Activity2):

package se.snapcode.lollipoptest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;


public class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_details);

        String header = getIntent().getStringExtra("header");
        String text = getIntent().getStringExtra("text");

        TextView tv1 = (TextView)findViewById(R.id.tv_details_header);
        tv1.setText(header);
        TextView tv2 = (TextView)findViewById(R.id.tv_details_text);
        tv2.setText(text);
    }
}

And the layouts, first my_text_view that is in the list of the RecyclerView:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    android:focusable="true"
    android:background="?android:attr/selectableItemBackground"
    android:colorControlHighlight="@android:color/holo_blue_light"
    android:padding="10dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="MY HEADER IS HERE"
        android:transitionName="header"
        android:id="@+id/textView1" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:text="This is some text that is of relevance"
        android:transitionName="text"
        android:id="@+id/textView2" />
</LinearLayout>

And the activity_details.xml:

<LinearLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="se.snapcode.lollipoptest.DetailsActivity">

    <TextView android:id="@+id/tv_details_header" android:text="A1" android:layout_width="wrap_content"
        android:transitionName="header"
        android:textSize="48dp"
        android:layout_height="wrap_content" />

    <TextView android:id="@+id/tv_details_text" android:text="Some text of lesser importance" android:layout_width="wrap_content"
        android:textSize="24dp"
        android:transitionName="text"
        android:layout_height="wrap_content" />

</LinearLayout>

And the transition xml (in /res/transition):

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds/>
    <explode />
</transitionSet>

and the styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="android:Theme.Material.Light">
        <!-- enable window content transitions -->
        <item name="android:windowContentTransitions">true</item>

        <!-- specify enter and exit transitions -->
        <item name="android:windowEnterTransition">@android:transition/slide_left</item>
        <item name="android:windowExitTransition">@android:transition/slide_right</item>

        <!-- specify shared element transitions -->
        <item name="android:windowSharedElementEnterTransition">
            @transition/change_image_transform</item>
        <item name="android:windowSharedElementExitTransition">
            @transition/change_image_transform</item>
    </style>
</resources>
1

There are 1 answers

5
Alex Lockwood On

The problem is that you are trying to animate a TextView's size as a shared element using a ChangeBounds transition. This will not work because of the way ChangeBounds works. The ChangeBounds transition analyzes the layout bounds of a view at the start and end of the transition and animates between the two. ChangeBounds works for arbitrary views, so it will not call setTextSize() on your TextView during the transition for you... this is something you would need to do yourself using a custom transition if you wanted to see the TextView's size seamlessly increase/decrease during the animation. There is some information on how to do this in this StackOverflow answer.