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

1

20

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>
Lineal answered 25/11, 2014 at 9:56 Comment(8)
I think this answer might help you: https://mcmap.net/q/530347/-how-can-i-scale-textviews-using-shared-element-transitionsEtte
Thanks for the input. So, defining animations/transitions in XML like in my case, doesnt work and is no good? Is that the officiel stance of google, you think?Lineal
The shared object transitions have proven to be a bit more cumbersome than I anticipated. My hope is that Google will improve the documentation because it is lacking.Ette
@Lineal How are you using the "transition xml (in /res/transition)" code snippet you are referencing in your question? I don't see why you would ever want to add an explode transition and a changeBounds transition in the same TransitionSet. explode animates views off the screen whereas changeBounds animates a view from one specific location/size to another within the screen... so running the two in parallel doesn't make any sense to me.Lupulin
@Lineal Also, you reference @transition/change_image_transform in your last code snippet. Can you please post the contents of your change_image_transform.xml file in your question?Lupulin
@AlexLockwood Sorry, the "And the transition xml (in /res/transition):..." is the change_image_transform.xml (has nothing to do with image transform though)Lineal
Yeah, so you'll definitely want to remove the explode animation from that transitionSet then... using an explode for a shared element transition doesn't make sense.Lupulin
I have tried different things as I mentioned in the question above, removing explode didnt change a thing =) I have read your linked post, but not tested.Lineal
L
12

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.

Lupulin answered 3/12, 2014 at 19:16 Comment(5)
So, the answer u linked to above details implementing a Transition class. I cant see how Google would think that anyone comes up with the idea to write such a class. Im a pretty experience coder, and I wouldnt have a clue how to write that class. It seems very cumbersome and messy.Lineal
But. basically, I copy that Transition class, and then just add that in the XML instead, like this? <transition class="com.package.name.TextSizeTransition" />Lineal
I just tried just that, added the java class, change the transition xml as above (with correct package name), but then the animation isnt there, just a jump from one activity to the next, same "glitch" with the text...Lineal
@Lineal The Android Framework can't always do everything you need. Sometimes you just need to write it yourself. :) The custom transition isn't actually all that complicated... it is basically just a wrapper around an Animator that animates a text view's TextSize. The only difficult part was learning how to write a custom Transition (as there aren't currently many docs explaining how this should be done). But this was pretty easy to figure out after I looked at the source code for the framework's default transitions.Lupulin
Aha, thanks for that input. However, I must be doing something wrong, as it is if I didnt do any animation at all, so, no effect with other words =) Sorry, I seem to be missing something.Lineal

© 2022 - 2024 — McMap. All rights reserved.