Search code examples
androidtextviewscaleobjectanimatoranimatorset

Android how to chain "exploding" text animations in textView?


I'm trying to chain together 3 "exploding" text animations in textView to show 3 words sequentially: "Ready", "Set" & "Go!". By "exploding", I mean text size goes from 0.25f of default to 1.00f of default while alpha=0 to alpha=1.

Problem: I'm able to get the first word "Ready" to "explode" as intended but the next word "Set" does not "explode" ie does not change text size at all (only alpha part of animation works).

My MainActivity.java is as follows. I have not put in the third "explosion" since if I could get the 2nd one to work it is a matter of copy & paste.

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    public void actionExplode(View view) {

        // .setTextSize() defaults to sp but .getTextSize() defaults to px http://stackoverflow.com/a/3687385/1827488

        String textReady = "Ready";
        final String textSet = "Set";
        final String textGo =  "Go!";

        final TextView questionDisplay = (TextView) findViewById(R.id.textView);
        final float textSizePx = questionDisplay.getTextSize();
        Log.i("actionExplode", "textSizePx=" + textSizePx);

        final float scaleSmall = 0.25f;
        float scaleFull = 1.0f;
        final float fadeOut = 0f;
        float fadeIn = 1f;

        questionDisplay.setAlpha(fadeOut);
        questionDisplay.setText(textReady);
        questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
        Log.i("actionExplode", ".getTextSize()=" + questionDisplay.getTextSize());

        int animateDurationReadySetGo = 1000;
        int animateDurationFudge = 100;

        ObjectAnimator animateReadyFadeIn = ObjectAnimator.ofFloat(questionDisplay, "alpha", fadeOut, fadeIn);
        animateReadyFadeIn.setDuration(animateDurationReadySetGo);
        ObjectAnimator animateReadyX = ObjectAnimator.ofFloat(questionDisplay, "scaleX", scaleFull/scaleSmall);
        animateReadyX.setDuration(animateDurationReadySetGo);
        ObjectAnimator animateReadyY = ObjectAnimator.ofFloat(questionDisplay, "scaleY", scaleFull/scaleSmall);
        animateReadyY.setDuration(animateDurationReadySetGo /* + animateDurationFudge */ );
        animateReadyY.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                Log.i("onAnimationEnd", "before .getTextSize()=" + questionDisplay.getTextSize());
                //questionDisplay.setAlpha(fadeOut);
                questionDisplay.setText(textSet);
                questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
            }
        });

        ObjectAnimator animateSetFadeIn = ObjectAnimator.ofFloat(questionDisplay, "alpha", fadeOut, fadeIn);
        animateSetFadeIn.setDuration(animateDurationReadySetGo);
        ObjectAnimator animateSetX = ObjectAnimator.ofFloat(questionDisplay, "scaleX", scaleFull/scaleSmall);
        animateSetX.setDuration(animateDurationReadySetGo);
        ObjectAnimator animateSetY = ObjectAnimator.ofFloat(questionDisplay, "scaleY", scaleFull/scaleSmall);
        animateSetY.setDuration(animateDurationReadySetGo /* + animateDurationFudge */ );
        animateSetY.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                Log.i("onAnimationEnd", "before .getTextSize()=" + questionDisplay.getTextSize());
                //questionDisplay.setAlpha(fadeOut);
                questionDisplay.setText(textGo);
                questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
            }
        });

        AnimatorSet animateReadySetGo = new AnimatorSet();
        animateReadySetGo.playTogether(animateReadyX, animateReadyY, animateReadyFadeIn);
        animateReadySetGo.playTogether(animateSetX, animateSetY, animateSetFadeIn);
        animateReadySetGo.playSequentially(animateReadyY, animateSetY);
        animateReadySetGo.start();
    }

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

This is what the logs show. What do not make sense are: 1) why the "before" lines show 43.75 when they should show 175.0? and 2) why the "after" lines show 43.75 but the text size does not shrink?

I/actionExplode: textSizePx=175.0
I/actionExplode: .getTextSize()=43.75
I/onAnimationEnd: before .getTextSize()=43.75
I/onAnimationEnd: after .getTextSize()=43.75
I/onAnimationEnd: before .getTextSize()=43.75
I/onAnimationEnd: after .getTextSize()=43.75

My 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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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.plaudev.explodingtext.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:text="@string/textView"
        android:id="@+id/textView"
        android:fontFamily="casual"
        android:textSize="50sp"
        android:textStyle="normal|bold"
        android:textAlignment="center"
        android:gravity="center" />

    <Button
        android:text="@string/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:id="@+id/button"
        android:background="@color/colourTransparent"
        android:textAllCaps="false"
        android:textSize="25sp"
        android:textStyle="normal|bold"
        android:fontFamily="casual"
        android:onClick="actionExplode"
        android:textColor="@android:color/holo_green_dark" />
</RelativeLayout>

Update 1: Still have not found solution but want to note this behaviour. If I rewrite actionExplode() to be run recursively and use something else as the onClick to start things off so my MainActivity class is as follows, then I can get the chain of 3 "explosions" but each is starting from a successively smaller text size as shown in additional log below. It seems that somehow by making the outlet questionDisplay final so it can be used by listeners or timers to chain animations is causing the outlet to retain text size even though I could still change its text (ie only selectively final). Thus I also tried a few variations (as noted in code comments) but none of them get me any closer to the intended behaviour than variation A.

public class MainActivity extends AppCompatActivity {

    String[] explosionChain = {"Ready", "Set", "Go!"};
    int explosionIndex = 0;
    int animateDurationExplosion = 1000;
    int animateDurationFudge = 100;

    public void actionExplode(final float textSizeFullPx, final int explosionIndex) {

        final TextView questionDisplay = (TextView) findViewById(R.id.textView);
        float textSizePx = questionDisplay.getTextSize();
        Log.i("actionExplode", "textSizeFullPx=" + textSizeFullPx + ", explosionIndex=" + explosionIndex + ", textSizePx=" + textSizePx);

        float scaleSmall = 0.25f;
        float scaleFull = 1.0f;
        float fadeOut = 0f;
        float fadeIn = 1f;

        questionDisplay.setAlpha(fadeOut);
        questionDisplay.setText(explosionChain[explosionIndex]);
        // variation A
        //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizeFullPx * scaleSmall);
        // variation B
        questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
        Log.i("actionExplode", ".getTextSize()=" + questionDisplay.getTextSize());

        ObjectAnimator animateFadeIn = ObjectAnimator.ofFloat(questionDisplay, "alpha", fadeOut, fadeIn);
        animateFadeIn.setDuration(animateDurationExplosion);
        ObjectAnimator animateScaleX = ObjectAnimator.ofFloat(questionDisplay, "scaleX", scaleFull/scaleSmall);
        animateScaleX.setDuration(animateDurationExplosion);
        ObjectAnimator animateScaleY = ObjectAnimator.ofFloat(questionDisplay, "scaleY", scaleFull/scaleSmall);
        animateScaleY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );

        AnimatorSet animateExplosion = new AnimatorSet();
        animateExplosion.playTogether(animateScaleX, animateScaleY, animateFadeIn);
        animateExplosion.start();

        CountDownTimer explodeNext = new CountDownTimer(animateDurationExplosion, animateDurationExplosion) {
            @Override
            public void onTick(long millisUntilFinished) {
            }
            @Override
            public void onFinish() {
                // variation C
                Log.i("onFinish", ".getTextSize()=" + questionDisplay.getTextSize());
                // variation D
                //Log.i("onFinish", "before .getTextSize()=" + questionDisplay.getTextSize());
                //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizeFullPx);
                //Log.i("onFinish", "after .getTextSize()=" + questionDisplay.getTextSize());
                if ((explosionIndex + 1) < explosionChain.length) {
                    actionExplode(textSizeFullPx, explosionIndex + 1);
                }
            }
        };
        explodeNext.start();
    }

    public void startChainExplosion(View view) {

        final TextView questionDisplay = (TextView) findViewById(R.id.textView);
        final float textSizeFullPx = questionDisplay.getTextSize();
        Log.i("startChainExplosion", "explosionIndex=" + explosionIndex + ", textSizeFullPx=" + textSizeFullPx);

        actionExplode(textSizeFullPx, explosionIndex);

    }

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

The updated code above produces this log:

I/startChainExplosion: explosionIndex=0, textSizeFullPx=175.0
I/actionExplode: textSizeFullPx=175.0, explosionIndex=0, textSizePx=175.0
I/actionExplode: .getTextSize()=43.75
I/onFinish: .getTextSize()=43.75
I/actionExplode: textSizeFullPx=175.0, explosionIndex=1, textSizePx=43.75
I/actionExplode: .getTextSize()=10.9375
I/onFinish: .getTextSize()=10.9375
I/actionExplode: textSizeFullPx=175.0, explosionIndex=2, textSizePx=10.9375
I/actionExplode: .getTextSize()=2.734375
I/onFinish: .getTextSize()=2.734375

Update 2: Following @Xaver's advice, tried using this as the onClick. But the result is the same as my initial attempt, ie "Ready" explodes but "Set" and "Go!" don't. Moreover, text size becomes really big (I'm guessing 175px * 4) after all animation is done. Updated code & log as follows. I have a feeling I need each word to have its own textView to avoid this text size retention issue.

public void explodeSequentially(View view) {

        String textReady = "Ready";
        final String textSet = "Set";
        final String textGo =  "Go!";

        final TextView questionDisplay = (TextView) findViewById(R.id.textView);
        float textSizePx = questionDisplay.getTextSize();
        Log.i("explodeSequentially", "textSizePx=" + textSizePx);

        float scaleSmall = 0.25f;
        float scaleFull = 1.0f;
        float fadeOut = 0f;
        float fadeIn = 1f;

        questionDisplay.setAlpha(fadeOut);
        questionDisplay.setText(textReady);
        questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
        Log.i("explodeSequentially", ".getTextSize()=" + questionDisplay.getTextSize());

        ObjectAnimator animateReadyFadeIn = ObjectAnimator.ofFloat(questionDisplay, "alpha", fadeOut, fadeIn);
        animateReadyFadeIn.setDuration(animateDurationExplosion);
        ObjectAnimator animateReadyX = ObjectAnimator.ofFloat(questionDisplay, "scaleX", scaleFull/scaleSmall);
        animateReadyX.setDuration(animateDurationExplosion);
        ObjectAnimator animateReadyY = ObjectAnimator.ofFloat(questionDisplay, "scaleY", scaleFull/scaleSmall);
        animateReadyY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );
        animateReadyY.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                Log.i("onAnimationEnd", "before .getTextSize()=" + questionDisplay.getTextSize());
                //questionDisplay.setAlpha(fadeOut);
                questionDisplay.setText(textSet);
                //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                //Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
            }
        });
        AnimatorSet animateReady = new AnimatorSet();
        animateReady.playTogether(animateReadyX, animateReadyY, animateReadyFadeIn);

        ObjectAnimator animateSetFadeIn = ObjectAnimator.ofFloat(questionDisplay, "alpha", fadeOut, fadeIn);
        animateSetFadeIn.setDuration(animateDurationExplosion);
        ObjectAnimator animateSetX = ObjectAnimator.ofFloat(questionDisplay, "scaleX", scaleFull/scaleSmall);
        animateSetX.setDuration(animateDurationExplosion);
        ObjectAnimator animateSetY = ObjectAnimator.ofFloat(questionDisplay, "scaleY", scaleFull/scaleSmall);
        animateSetY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );
        animateSetY.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                Log.i("onAnimationEnd", "before .getTextSize()=" + questionDisplay.getTextSize());
                //questionDisplay.setAlpha(fadeOut);
                questionDisplay.setText(textGo);
                //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                //Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
            }
        });
        AnimatorSet animateSet = new AnimatorSet();
        animateSet.playTogether(animateSetX, animateSetY, animateSetFadeIn);

        ObjectAnimator animateGoFadeIn = ObjectAnimator.ofFloat(questionDisplay, "alpha", fadeOut, fadeIn);
        animateGoFadeIn.setDuration(animateDurationExplosion);
        ObjectAnimator animateGoX = ObjectAnimator.ofFloat(questionDisplay, "scaleX", scaleFull/scaleSmall);
        animateGoX.setDuration(animateDurationExplosion);
        ObjectAnimator animateGoY = ObjectAnimator.ofFloat(questionDisplay, "scaleY", scaleFull/scaleSmall);
        animateGoY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );
        animateGoY.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                Log.i("onAnimationEnd", "before .getTextSize()=" + questionDisplay.getTextSize());
                //questionDisplay.setAlpha(fadeOut);
                questionDisplay.setText("Here is the question!");
                //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                //Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
            }
        });
        AnimatorSet animateGo = new AnimatorSet();
        animateGo.playTogether(animateGoX, animateGoY, animateGoFadeIn);

        AnimatorSet animateReadySetGo = new AnimatorSet();
        animateReadySetGo.playSequentially(animateReady, animateSet, animateGo);
        animateReadySetGo.start();
    }

And the logs

I/explodeSequentially: textSizePx=175.0
I/explodeSequentially: .getTextSize()=43.75
I/onAnimationEnd: before .getTextSize()=43.75
I/onAnimationEnd: before .getTextSize()=43.75
I/onAnimationEnd: before .getTextSize()=43.75

Solution

  • My suspicion was correct. Text size on textViews linger whenever final is used on the outlets and such use seems unavoidable if I want to chain animations. However by splitting each word I want to "explode" into its own textView, at least I can get the "explosion" effect to work in sequence as intended.

    The following code will achieve the intended animation sequence only once, ie the next time you click the button, the animation sequence will start from 43.75px instead of the initial 175.0px, and continues to decrease at 0.25f factor each further time you click. I will amend the code to dynamically create & destroy the textViews to get around that later (now updated below).

    The new MainActivity.java:

    public class MainActivity extends AppCompatActivity {
    
        public void explodeThreeTextViews (View view) {
    
            int animateDurationExplosion = 1000;
            float scaleSmall = 0.25f;
            float scaleFull = 1.0f;
            float fadeOut = 0f;
            final float fadeIn = 1f;
    
            final TextView tvQuestion = (TextView) findViewById(R.id.question);
            final TextView tvReady = (TextView) findViewById(R.id.ready);
            final TextView tvSet = (TextView) findViewById(R.id.set);
            final TextView tvGo = (TextView) findViewById(R.id.go);
    
            float tvReadySizePx = tvReady.getTextSize();
            float tvSetSizePx = tvSet.getTextSize();
            float tvGoSizePx = tvGo.getTextSize();
            Log.i("explodeThreeTextViews", "tvReadySizePx=" + tvReadySizePx + ", tvSetSizePx=" + tvSetSizePx + ", tvGoSizePx=" + tvGoSizePx);
    
            tvQuestion.setAlpha(fadeOut);
            tvReady.setAlpha(fadeOut);
            tvSet.setAlpha(fadeOut);
            tvGo.setAlpha(fadeOut);
    
            tvQuestion.setText("The question!");
            tvReady.setText("Ready");
            tvSet.setText("Set");
            tvGo.setText("Go!");
    
            tvQuestion.setVisibility(View.GONE);
            tvReady.setVisibility(View.VISIBLE);
    
            tvReady.setTextSize(TypedValue.COMPLEX_UNIT_PX, tvReadySizePx * scaleSmall);
            tvSet.setTextSize(TypedValue.COMPLEX_UNIT_PX, tvSetSizePx * scaleSmall);
            tvGo.setTextSize(TypedValue.COMPLEX_UNIT_PX, tvGoSizePx * scaleSmall);
            Log.i("explodeThreeTextViews", "tvReady.getTextSize=" + tvReady.getTextSize() + ", tvSet.getTextSize=" + tvSet.getTextSize() + ", tvGo.getTextSize=" + tvGo.getTextSize());
    
            ObjectAnimator animateReadyFadeIn = ObjectAnimator.ofFloat(tvReady, "alpha", fadeOut, fadeIn);
            animateReadyFadeIn.setDuration(animateDurationExplosion);
            ObjectAnimator animateReadyX = ObjectAnimator.ofFloat(tvReady, "scaleX", scaleFull/scaleSmall);
            animateReadyX.setDuration(animateDurationExplosion);
            ObjectAnimator animateReadyY = ObjectAnimator.ofFloat(tvReady, "scaleY", scaleFull/scaleSmall);
            animateReadyY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );
            animateReadyY.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    Log.i("onAnimationEnd", "tvReady.getTextSize()=" + tvReady.getTextSize());
                    //questionDisplay.setAlpha(fadeOut);
                    tvReady.setVisibility(View.GONE);
                    tvSet.setVisibility(View.VISIBLE);
                    //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                    //Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
                }
            });
            AnimatorSet animateReady = new AnimatorSet();
            animateReady.playTogether(animateReadyX, animateReadyY, animateReadyFadeIn);
    
            ObjectAnimator animateSetFadeIn = ObjectAnimator.ofFloat(tvSet, "alpha", fadeOut, fadeIn);
            animateSetFadeIn.setDuration(animateDurationExplosion);
            ObjectAnimator animateSetX = ObjectAnimator.ofFloat(tvSet, "scaleX", scaleFull/scaleSmall);
            animateSetX.setDuration(animateDurationExplosion);
            ObjectAnimator animateSetY = ObjectAnimator.ofFloat(tvSet, "scaleY", scaleFull/scaleSmall);
            animateSetY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );
            animateSetY.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    Log.i("onAnimationEnd", "tvSet.getTextSize()=" + tvSet.getTextSize());
                    //questionDisplay.setAlpha(fadeOut);
                    tvSet.setVisibility(View.GONE);
                    tvGo.setVisibility(View.VISIBLE);
                    //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                    //Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
                }
            });
            AnimatorSet animateSet = new AnimatorSet();
            animateSet.playTogether(animateSetX, animateSetY, animateSetFadeIn);
    
            ObjectAnimator animateGoFadeIn = ObjectAnimator.ofFloat(tvGo, "alpha", fadeOut, fadeIn);
            animateGoFadeIn.setDuration(animateDurationExplosion);
            ObjectAnimator animateGoX = ObjectAnimator.ofFloat(tvGo, "scaleX", scaleFull/scaleSmall);
            animateGoX.setDuration(animateDurationExplosion);
            ObjectAnimator animateGoY = ObjectAnimator.ofFloat(tvGo, "scaleY", scaleFull/scaleSmall);
            animateGoY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );
            animateGoY.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    Log.i("onAnimationEnd", "tvGo.getTextSize()=" + tvGo.getTextSize());
                    //questionDisplay.setAlpha(fadeOut);
                    tvGo.setVisibility(View.GONE);
                    tvQuestion.setVisibility(View.VISIBLE);
                    tvQuestion.setAlpha(fadeIn);
                    //questionDisplay.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
                    //Log.i("onAnimationEnd", "after .getTextSize()=" + questionDisplay.getTextSize());
                }
            });
            AnimatorSet animateGo = new AnimatorSet();
            animateGo.playTogether(animateGoX, animateGoY, animateGoFadeIn);
    
            AnimatorSet animateReadySetGo = new AnimatorSet();
            animateReadySetGo.playSequentially(animateReady, animateSet, animateGo);
            animateReadySetGo.start();
        }
    
    @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    

    The new 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:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        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.plaudev.explodingtext.MainActivity">
    
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:id="@+id/questionDisplay">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="@string/textView"
                android:id="@+id/question"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:textAlignment="center"
                android:gravity="center"
                android:layout_weight="1"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="@string/textView"
                android:id="@+id/ready"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:textAlignment="center"
                android:gravity="center"
                android:layout_weight="1"
                android:visibility="gone"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="@string/textView"
                android:id="@+id/set"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:textAlignment="center"
                android:gravity="center"
                android:layout_weight="1"
                android:visibility="gone"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="@string/textView"
                android:id="@+id/go"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:textAlignment="center"
                android:gravity="center"
                android:layout_weight="1"
                android:visibility="gone"/>
    
            <Button
                android:text="@string/button"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:id="@+id/button"
                android:background="@color/colourTransparent"
                android:textAllCaps="false"
                android:textSize="25sp"
                android:textStyle="normal|bold"
                android:fontFamily="casual"
                android:onClick="explodeThreeTextViews"
                android:textColor="@android:color/holo_green_dark"
                android:layout_weight="1" />
    
        </LinearLayout>
    
    </RelativeLayout>
    

    The log when you run this:

    I/explodeThreeTextViews: tvReadySizePx=175.0, tvSetSizePx=175.0, tvGoSizePx=175.0
    I/explodeThreeTextViews: tvReady.getTextSize=43.75, tvSet.getTextSize=43.75, tvGo.getTextSize=43.75
    I/onAnimationEnd: tvReady.getTextSize()=43.75
    I/onAnimationEnd: tvSet.getTextSize()=43.75
    I/onAnimationEnd: tvGo.getTextSize()=43.75
    

    Update: With dynamically created & destroyed views to enable repeated use based on the recursive method tried prior.

    Updated MainActivity.java - you have a choice of using animation listener or CountDownTimer to chain the "explosions":

    public class MainActivity extends AppCompatActivity {
    
        // constants
        String[] explosionChain = {"Ready", "Set", "Go!"};
        float scaleSmall = 0.25f;
        float scaleFull = 1.0f;
        float fadeOut = 0f;
        float fadeIn = 1f;
        int animateDurationExplosion = 1000;
        int animateDurationFudge = 100;
    
        // variables
        float textSizeFullPx;
        TextView[] explosionViews;
        int explosionIndex;
    
        public void prepareChainExplosions(View view) {
    
            //String typeface = "casual";
    
            LinearLayout questionDisplay = (LinearLayout) findViewById(R.id.questionDisplay);
            TextView questionView = (TextView) findViewById(R.id.questionView);
            textSizeFullPx = questionView.getTextSize();
            Log.i("prepareChainExplosions", "textSizeFullPx=" + textSizeFullPx);
    
            explosionViews = new TextView[explosionChain.length];
            for (int i = 0; i < explosionViews.length; i++) {
                explosionViews[i] = new TextView(questionDisplay.getContext());
                explosionViews[i].setVisibility(View.GONE);
                explosionViews[i].setAlpha(fadeOut);
                //explosionViews[i].setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, 1f));
                explosionViews[i].setLayoutParams(questionView.getLayoutParams());
                //explosionViews[i].setGravity(Gravity.CENTER);
                explosionViews[i].setGravity(questionView.getGravity());
                //explosionViews[i].setTypeface(Typeface.create(typeface, Typeface.BOLD));
                explosionViews[i].setTypeface(questionView.getTypeface());
                explosionViews[i].setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizeFullPx);
                explosionViews[i].setText(explosionChain[i]);
                questionDisplay.addView(explosionViews[i]);
            }
            Log.i("prepareChainExplosions", "questionDisplay.getChildCount()=" + questionDisplay.getChildCount());
        }
    
    public void actionExplode(final int explosionIndex) {
    
            final TextView questionView = (TextView) findViewById(R.id.questionView);
            final TextView explodingView = explosionViews[explosionIndex];
            float textSizePx = explodingView.getTextSize();
            Log.i("actionExplode", "explosionIndex=" + explosionIndex + ", textSizePx=" + textSizePx);
    
            explodingView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx * scaleSmall);
            Log.i("actionExplode", ".getTextSize()=" + explodingView.getTextSize());
    
            if (explosionIndex == 0) questionView.setVisibility(View.GONE);
            explodingView.setVisibility(View.VISIBLE);
    
            ObjectAnimator animateFadeIn = ObjectAnimator.ofFloat(explodingView, "alpha", fadeOut, fadeIn);
            animateFadeIn.setDuration(animateDurationExplosion);
            ObjectAnimator animateScaleX = ObjectAnimator.ofFloat(explodingView, "scaleX", scaleFull/scaleSmall);
            animateScaleX.setDuration(animateDurationExplosion);
            ObjectAnimator animateScaleY = ObjectAnimator.ofFloat(explodingView, "scaleY", scaleFull/scaleSmall);
            animateScaleY.setDuration(animateDurationExplosion /* + animateDurationFudge */ );
            animateScaleY.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    Log.i("onAnimationEnd", ".getTextSize()=" + explodingView.getTextSize());
                    explodingView.setVisibility(View.GONE);
                    if ((explosionIndex + 1) < explosionChain.length) {
                        actionExplode(explosionIndex + 1);
                    } else {
                        questionView.setVisibility(View.VISIBLE);
                    }
                }
            });
    
            AnimatorSet animateExplosion = new AnimatorSet();
            animateExplosion.playTogether(animateScaleX, animateScaleY, animateFadeIn);
            animateExplosion.start();
    
            /*
            CountDownTimer explodeNext = new CountDownTimer(animateDurationExplosion, animateDurationExplosion) {
                @Override
                public void onTick(long millisUntilFinished) {
                }
                @Override
                public void onFinish() {
                    Log.i("onFinish", ".getTextSize()=" + explodingView.getTextSize());
                    explodingView.setVisibility(View.GONE);
                    if ((explosionIndex + 1) < explosionChain.length) {
                        actionExplode(explosionIndex + 1);
                    } else {
                        questionView.setVisibility(View.VISIBLE);
                    }
                }
            };
            explodeNext.start();
            */
        }
    
        public void startChainExplosions(View view) {
    
            prepareChainExplosions(view);
            explosionIndex = 0;
            Log.i("startChainExplosions", "explosionIndex=" + explosionIndex);
    
            actionExplode(explosionIndex);
    
            CountDownTimer cleanChainExplosions = new CountDownTimer(animateDurationExplosion * explosionChain.length, animateDurationExplosion) {
                @Override
                public void onTick(long millisUntilFinished) {
                }
                @Override
                public void onFinish() {
                    for (int i = 0; i < explosionViews.length; i++) {
                        ((ViewManager) explosionViews[i].getParent()).removeView(explosionViews[i]);
                    }
                    LinearLayout questionDisplay = (LinearLayout) findViewById(R.id.questionDisplay);
                    Log.i("startChainExplosions", "questionDisplay.getChildCount()=" + questionDisplay.getChildCount());
                }
            };
            cleanChainExplosions.start();
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    

    Updated activity_main.xml - some redundant textViews are kept for backward compatibility with prior code.

    <?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:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        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.plaudev.explodingtext.MainActivity">
    
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:id="@+id/questionDisplay">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="@string/textView"
                android:id="@+id/questionView"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:layout_weight="1"
                android:gravity="center" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="@string/textView"
                android:id="@+id/readyView"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:textAlignment="center"
                android:gravity="center"
                android:layout_weight="1"
                android:visibility="gone"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="@string/textView"
                android:id="@+id/setView"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:textAlignment="center"
                android:gravity="center"
                android:layout_weight="1"
                android:visibility="gone"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="@string/textView"
                android:id="@+id/goView"
                android:fontFamily="casual"
                android:textSize="50sp"
                android:textStyle="normal|bold"
                android:textAlignment="center"
                android:gravity="center"
                android:layout_weight="1"
                android:visibility="gone"/>
    
        </LinearLayout>
    
        <Button
            android:text="@string/button"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:id="@+id/button"
            android:background="@color/colourTransparent"
            android:textAllCaps="false"
            android:textSize="25sp"
            android:textStyle="normal|bold"
            android:fontFamily="casual"
            android:onClick="startChainExplosions"
            android:textColor="@android:color/holo_green_dark"
            android:layout_below="@+id/questionDisplay"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true" />
    
    </RelativeLayout>
    

    Updated log:

    I/prepareChainExplosions: textSizeFullPx=175.0
    I/prepareChainExplosions: questionDisplay.getChildCount()=7
    I/startChainExplosions: explosionIndex=0
    I/actionExplode: explosionIndex=0, textSizePx=175.0
    I/actionExplode: .getTextSize()=43.75
    I/onAnimationEnd: .getTextSize()=43.75
    I/actionExplode: explosionIndex=1, textSizePx=175.0
    I/actionExplode: .getTextSize()=43.75
    I/onAnimationEnd: .getTextSize()=43.75
    I/actionExplode: explosionIndex=2, textSizePx=175.0
    I/actionExplode: .getTextSize()=43.75
    I/startChainExplosions: questionDisplay.getChildCount()=4
    I/onAnimationEnd: .getTextSize()=43.75