Skip to content

Instantly share code, notes, and snippets.

@richarddd
Created November 11, 2015 14:22
Show Gist options
  • Save richarddd/c5e4c83c14565306140b to your computer and use it in GitHub Desktop.
Save richarddd/c5e4c83c14565306140b to your computer and use it in GitHub Desktop.
Shared Transition Text
import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Parcelable;
import android.transition.Transition;
import android.transition.TransitionSet;
import android.transition.TransitionValues;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
/**
* Created by richard on 24/10/15.
*/
@SuppressLint("NewApi")
public class ChangeText extends Transition {
private static final String PROPNAME_TEXTSIZE = "android:resizetext:textSize";
private static final String PROPNAME_TEXTCOLOR = "android:resizetext:textColor";
private static final String PROPNAME_TEXTPADDING = "android:resizetext:textPadding";
private static final String KEY_TEXT_SIZES = "key_text_sizes";
private static final String KEY_TEXT_COLORS = "key_text_colors";
private static final String KEY_TEXT_PADDINGS = "key_text_padding";
private static final String KEY_TEXT_NAMES = "key_text_names";
private static final String TAG = ChangeText.class.getSimpleName();
private List<String> keyNames;
private float[] sizeValues;
private int[] colorValues;
private Rect[] paddingValues;
private boolean enter;
@Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues, "start");
}
private void captureValues(TransitionValues transitionValues, String log) {
View view = transitionValues.view;
if (view.getVisibility() == View.GONE) {
return;
}
int index = keyNames.indexOf(view.getTransitionName());
if (index > -1) {
Log.d(TAG,log+"direction = "+enter);
float textSize = sizeValues[index];
int textColor = colorValues[index];
Rect padding = paddingValues[index];
transitionValues.values.put(PROPNAME_TEXTSIZE, textSize);
transitionValues.values.put(PROPNAME_TEXTCOLOR, textColor);
transitionValues.values.put(PROPNAME_TEXTPADDING, padding);
}
}
public ChangeText() {
init();
}
public ChangeText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
addTarget(TextView.class);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || startValues.values.size() == 0) {
return null;
}
final TextView textView = ((TextView) startValues.view);
int fromColor = (int) startValues.values.get(PROPNAME_TEXTCOLOR);
int toColor = textView.getTextColors().getDefaultColor();
float fromSize = (float) startValues.values.get(PROPNAME_TEXTSIZE);
float toSize = textView.getTextSize();
Rect fromPadding = (Rect)startValues.values.get(PROPNAME_TEXTPADDING);
Rect toPadding = paddingToRect(textView);
if(!enter){
fromColor = fromColor + toColor;
toColor = fromColor - toColor;
fromColor = fromColor - toColor;
fromSize = fromSize + toSize;
toSize = fromSize - toSize;
fromSize = fromSize - toSize;
Rect temp = new Rect(toPadding);
toPadding = fromPadding;
fromPadding = temp;
}
final Rect finalFromPadding = fromPadding;
final Rect finalToPadding = toPadding;
final float finalFromSize = fromSize;
final float finalToSize = toSize;
ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(),
fromColor, toColor);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Object value = animation.getAnimatedValue();
float fraction = animation.getAnimatedFraction();
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fracturedValue(fraction, finalFromSize, finalToSize));
textView.setPadding(fracturedValue(fraction, finalFromPadding.left, finalToPadding.left), fracturedValue(fraction, finalFromPadding.top, finalToPadding.top), fracturedValue(fraction, finalFromPadding.right, finalToPadding.right), fracturedValue(fraction, finalFromPadding.bottom, finalToPadding.bottom));
if (null != value) {
textView.setTextColor((Integer) value);
}
}
});
return animator;
}
private static Rect paddingToRect(TextView textView) {
return new Rect(textView.getPaddingLeft(),textView.getPaddingTop(),textView.getPaddingRight(),textView.getPaddingBottom());
}
private static float fracturedValue(float fraction, float from, float to) {
return from + ((to-from)*fraction);
}
private static int fracturedValue(float fraction, int from, int to) {
return (int) fracturedValue(fraction,(float)from,(float)to);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues, "end");
}
public static void intent(Intent intent, View sharedView, String sharedElementName) {
intent(intent, new Pair<View, String>(sharedView, sharedElementName));
}
public static void intent(Intent intent, Pair<View, String>... sharedElements) {
float[] sizeArray = new float[sharedElements.length];
int[] colorArray = new int[sharedElements.length];
Rect[] paddingArray = new Rect[sharedElements.length];
String[] keyArray = new String[sharedElements.length];
boolean hasValue = false;
int index = 0;
for (Pair<View, String> sharedElement : sharedElements) {
View view = sharedElement.first;
if (view instanceof TextView) {
TextView textView = ((TextView) sharedElement.first);
hasValue = true;
sizeArray[index] = textView.getTextSize();
colorArray[index] = textView.getTextColors().getDefaultColor();
paddingArray[index] = paddingToRect(textView);
keyArray[index] = sharedElement.second;
}
index++;
}
if (hasValue) {
intent.putExtra(KEY_TEXT_SIZES, sizeArray);
intent.putExtra(KEY_TEXT_COLORS, colorArray);
intent.putExtra(KEY_TEXT_PADDINGS,paddingArray);
intent.putExtra(KEY_TEXT_NAMES, keyArray);
}
}
public static <T extends Parcelable> T[] getParcelableArray(Intent bundle, String key, Class<T[]> type) {
Parcelable[] value = bundle.getParcelableArrayExtra(key);
if (value == null) {
return null;
}
Object copy = Array.newInstance(type.getComponentType(), value.length);
System.arraycopy(value, 0, copy, 0, value.length);
return (T[]) copy;
}
public static void setup(Activity activity) {
Intent intent = activity.getIntent();
if (intent.hasExtra(KEY_TEXT_NAMES)) {
Transition transitionEnter = activity.getWindow().getSharedElementEnterTransition();
Transition transitionExit = activity.getWindow().getSharedElementReturnTransition();
float[] sizeValues = intent.getFloatArrayExtra(KEY_TEXT_SIZES);
int[] colorValues = intent.getIntArrayExtra(KEY_TEXT_COLORS);
Rect[] paddingValues = getParcelableArray(intent,KEY_TEXT_PADDINGS,Rect[].class);
List<String> keyNames = Arrays.asList(intent.getStringArrayExtra(KEY_TEXT_NAMES));
setupValues(keyNames, sizeValues, colorValues, paddingValues,transitionEnter, true);
setupValues(keyNames, sizeValues, colorValues, paddingValues,transitionExit, false);
}
}
private static void setupValues(List<String> keyNames, float[] sizeValues, int[] colorValues, Rect[] paddingValues, Transition transition, boolean enter) {
if (transition != null) {
ChangeText changeTextTransition = null;
if (transition instanceof TransitionSet) {
TransitionSet set = (TransitionSet) transition;
for (int i = 0; i < set.getTransitionCount(); i++) {
Transition transitionInSet = set.getTransitionAt(i);
if (transitionInSet instanceof ChangeText) {
changeTextTransition = (ChangeText) transitionInSet;
break;
}
}
} else if (!(transition instanceof ChangeText)) {
return;
} else {
changeTextTransition = (ChangeText) transition;
}
if (changeTextTransition != null) {
changeTextTransition.setValues(keyNames, sizeValues, colorValues, paddingValues,enter);
}
}
}
private void setValues(List<String> keyNames, float[] sizeValues, int[] colorValues, Rect[] paddingValues, boolean enter) {
this.keyNames = keyNames;
this.sizeValues = sizeValues;
this.colorValues = colorValues;
this.paddingValues = paddingValues;
this.enter = enter;
}
}
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:duration="450">
<changeBounds />
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
<transition class="com.yourpackage.transition.ChangeText" />
<arcMotion android:minimumHorizontalAngle="50" android:minimumVerticalAngle="50" />
</transitionSet>
public static void startActivity(Activity activity, int intentParamterOrSomething, View sharedView){
Intent intent = new Intent(activity, DetailActivity.class);
intent.putExtra(EXTRA_WHATEVER, intentParamterOrSomething);
Bundle bundle = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, sharedView, sharedView.getTransitionName()).toBundle();
ChangeText.intent(intent, sharedView, sharedView.getTransitionName());
}
ActivityCompat.startActivity(activity, intent, bundle);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setSharedElementEnterTransition(TransitionInflater.from(this)
.inflateTransition(R.transition.curve));
getWindow().setSharedElementReturnTransition(TransitionInflater.from(this)
.inflateTransition(R.transition.curve));
ChangeText.setup(this);
}
...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment