I am able to get TextViews to transition perfectly between two activities using ActivityOptions.makeSceneTransitionAnimation
. However I want to make the text scale up as it transitions. I can see the material design example scaling up the text "Alphonso Engelking" in the contact card transition.
I've tried setting the scale attributes on the destination TextView and using the changeTransform shared element transitions, but it doesn't scale and the text ends up being truncated as it transitions.
How can I scale TextViews using shared element transition?
As pointed out by Kiryl Tkach in the comments below, there is a better solution described in this Google I/O talk.
You can create a custom transition that animates a TextView
's text size as follows:
public class TextSizeTransition extends Transition {
private static final String PROPNAME_TEXT_SIZE = "alexjlockwood:transition:textsize";
private static final String[] TRANSITION_PROPERTIES = { PROPNAME_TEXT_SIZE };
private static final Property<TextView, Float> TEXT_SIZE_PROPERTY =
new Property<TextView, Float>(Float.class, "textSize") {
@Override
public Float get(TextView textView) {
return textView.getTextSize();
}
@Override
public void set(TextView textView, Float textSizePixels) {
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePixels);
}
};
public TextSizeTransition() {
}
public TextSizeTransition(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public String[] getTransitionProperties() {
return TRANSITION_PROPERTIES;
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues transitionValues) {
if (transitionValues.view instanceof TextView) {
TextView textView = (TextView) transitionValues.view;
transitionValues.values.put(PROPNAME_TEXT_SIZE, textView.getTextSize());
}
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Float startSize = (Float) startValues.values.get(PROPNAME_TEXT_SIZE);
Float endSize = (Float) endValues.values.get(PROPNAME_TEXT_SIZE);
if (startSize == null || endSize == null ||
startSize.floatValue() == endSize.floatValue()) {
return null;
}
TextView view = (TextView) endValues.view;
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, startSize);
return ObjectAnimator.ofFloat(view, TEXT_SIZE_PROPERTY, startSize, endSize);
}
}
Since changing the TextView
's text size will cause its layout bounds to change during the course of the animation, getting the transition to work properly will take a little more effort than simply throwing a ChangeBounds
transition into the same TransitionSet
. What you will need to do instead is manually measure/layout the view in its end state in a SharedElementCallback
.
I've published an example project on GitHub that illustrates the concept (note that the project defines two Gradle product flavors... one uses Activity Transitions and the other uses Fragment Transitions).