1 overridePendingTransiton

Android2.0引入,整个Activity界面的转场动画。

1.1 使用

紧跟在startActivity后面调用

startActivity(new Intent(this,MainActivity2.class));
//传入动画资源id,这里的动画是视图动画中的补间动画
//参数1:进入的Activity的动画
//参数2:退出的Activity的动画
overridePendingTransition(R.anim.up,R.anim.down);

#R.anim.up

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:toYDelta="0"
        android:fromYDelta="100%"
        android:duration="2000"/>
</set>

#R.anim.down

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:toYDelta="-100%"
        android:fromYDelta="0"
        android:duration="2000"/>
</set>

2 ActivityOptions

ActivityOptions 是一个启动Activity时的助手类,用来设置有关如何启动Activity的选项。在使用时会被转换成 Bundle 对象传入 startActivities 方法中。

#Context.java

public abstract void startActivities(@RequiresPermission Intent[] intents, Bundle options);

ActivityOptions 类中提供了以下静态方法来设置Activity的转场动画,这些方法会返回一个ActivityOptions对象。

方法名

说明

makeClipRevealAnimation(View source, int startX, int startY, int width, int height)

以一个点为中心开始揭露

makeCustomAnimation(Context context, int enterResId, int exitResId)

类似overridePendingTransition

makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)

放大一个Bitmap来过渡

makeScaleUpAnimation(View source, int startX, int startY, int width, int height)

以一个点为中心开始放大

** makeSceneTransitionAnimation(Activity activity)**

无共享元素过渡动画

** makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName)**

单个共享元素的过渡动画

** makeSceneTransitionAnimation(Activity activity, Pair<View, String>... sharedElements)**

多个共享元素的过渡动画

最常用的是最后三个方法,这三个方法会设置好前后Activity之间的共享元素,这样我们之后就能对共享元素和非共享元素(应该说是除了共享元素以外的元素,【下文统一用非共享元素代指除了共享元素以外的元素】)设置不同的动画效果。

2.1 设置共享元素

无共享元素

startActivity(
        new Intent(this,MainActivity1.class), 
        ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
);

单共享元素

startActivity(
    new Intent(
        this,
        MainActivity2.class
    ),
    ActivityOptions.makeSceneTransitionAnimation(this,buttonMoveUp,"b1").toBundle()
);

多共享元素

startActivity(
    new Intent(
        this,
        MainActivity3.class
    ),
    ActivityOptions.makeSceneTransitionAnimation(
        this,
        Pair.create(buttonMoveUp,"b1"), // 参数一:被设置成共享元素的View,参数二:取个名字
        Pair.create(buttonShare,"b2")
    ).toBundle()
);
2.2 什么是Transition

makeSceneTransitionAnimation()从方法名可以知道,使用了Transition来完成两个场景之间的变换。Andorid5.0引入了 TransitionTransition 被用在两个场景之间的UI变换,它会捕捉开始场景和结束场景中每个 View 的状态,然后创建Animator来完成两个场景之间的变换。

2.3 设置非共享的动画效果

也叫设置内容过渡动画,其实就是设置除了共享元素以外的元素的动画效果。

#Window.java

方法名

说明

void setEnterTransition(Transition transition)

可以理解成创建Activity时非共享元素的入场动画

void setReturnTransition(Transition transition)

可以理解成销毁Activity时非共享元素的退场动画

void setReenterTransition(Transition transition)

可以理解成Activity从后台回到前台时非共享元素的入场动画

void setExitTransition(Transition transition)

可以理解成Activity从前台退到后台时非共享元素的退场动画

2.4 设置共享元素的动画效果

#Window.java

方法名

说明

void setSharedElementEnterTransition(Transition transition)

可以理解成创建Activity时共享元素的入场动画

void setSharedElementReturnTransition(Transition transition)

可以理解成销毁Activity时共享元素的退场动画

void setSharedElementReenterTransition(Transition transition)

可以理解成Activity从后台回到前台时共享元素的入场动画

void setSharedElementExitTransition(Transition transition)

可以理解成Activity从前台退到后台时共享元素的退场动画

2.5 系统自带的Transition

以下 Transition通常被用来设置共享元素的动画效果

因为共享元素是一个从有到有的过程,而非共享元素是从无到有或者从有到无的过程,因此系统提供的Transition是不一样的。

类名

说明

ChangeBounds

在变换中更改布局边界,即改变View的大小和位置

ChangeClipBounds

在变换中更改View#getClipBounds()的值,即Canvas的范围内View的边界。

ChangeImageTransform

在变换中更改ImageViewMartix,同时完成动画,即改变大小位置。

ChangeTransform

在变换中更改View的缩放和旋转的值

ChangeScroll(需要API 23,Android M)

在变换中更改View关于Scroll相关的属性值

2.6 系统自带的Visibility

Visibility继承了 Transition,因此系统自带的 Visibility也属于系统自带的 Transition以下这几个 Transition通常用在设置非共享元素的动画效果。

类名

说明

Explode

分解/聚合动画,如果没有提供震中,会从焦点View和View的中点移入/移出。

Fade

淡入/淡出动画

Slide

滑入/滑出动画

2.7 使用示例

startActivity的代码部分前面参考前面设置。

  • 非共享元素转场动画

#MainActivity

Slide slideExit = new Slide();
slideExit.setDuration(2000);
slideExit.setSlideEdge(Gravity.START);
// 从前台退到后台的非共享元素的动画效果
getWindow().setExitTransition(slideExit);

#MainActivity2

// 设置是否动画有重叠,true时会尽快执行入场动画,以至于可能会和退场动画有重叠部分。false会等退场动画结束后在执行入场动画。
getWindow().setAllowEnterTransitionOverlap(false);
// 创建Activty时的非共享元素进场动画
Slide slideEnter = new Slide();
slideEnter.setDuration(2000);
slideEnter.setSlideEdge(Gravity.END);
getWindow().setEnterTransition(slideEnter);

//postponeEnterTransition(); //推迟入场动画启动
//startPostponedEnterTransition(); //开始被推迟的入场动画
  • 共享元素转场动画
ChangeBounds changeBounds = new ChangeBounds();
ChangeClipBounds changeClipBounds = new ChangeClipBounds();
ChangeTransform changeTransform = new ChangeTransform();
ChangeImageTransform changeImageTransform = new ChangeImageTransform();

//TransitionSet继承Transition,用于一次设置多个Transition
TransitionSet transitionSet = new TransitionSet();
transitionSet.addTransition(changeImageTransform);
transitionSet.addTransition(changeBounds);
transitionSet.addTransition(changeClipBounds);
transitionSet.addTransition(changeTransform);
transitionSet.setDuration(2000);

//设置创建Activity时的共享元素动画
getWindow().setSharedElementEnterTransition(transitionSet);

提到 TransitionSet 就为前面补充一个系统自带的 TransitionSet

#AutoTransition.java

public class AutoTransition extends TransitionSet {
    public AutoTransition() {
        init();
    }
    public AutoTransition(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private void init() {
        setOrdering(ORDERING_SEQUENTIAL);
        addTransition(new Fade(Fade.OUT)).
                addTransition(new ChangeBounds()).
                addTransition(new Fade(Fade.IN));
    }
}

3 自定义转场动画

3.1 自定义Transition
public class TestTransition extends Transition {
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
      	//这个方法是Transition用来获取开始状态的值
      	//我们通过向transitionValues.values.put方法提交开始状态的值
				//transitionValues.values是一个ArrayMap<String, Object>
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
				//这个方法是Transition用来获取结束状态的值
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        return super.createAnimator(sceneRoot, startValues, endValues);
      	//startValues对应captureStartValues的TransitionValues
      	//endValues对应captureEndValues的TransitionValues
    }
}

自定义Transition需要解决一个重要问题:如何获取起始状态的参数?最简单的方式就是在创建Transition的时候就传入起始状态需要的参数,但是需要前一个Activity(即开始状态)以及后一个Activity(即结束状态)的参数。而且参数多的话需要传入多个参数。

3.2 自定义Visibility

和继承Transition类似,VisibilityTransition多提供了 onAppearonDisappear方法。我们可以在 onAppear中实现从不可见变为可见(入场)的动画,在onDisappear中实现从可见为不可见(退场)的动画。

public class TestVisibility extends Visibility {
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        super.captureStartValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        super.captureEndValues(transitionValues);
    }

    @Override
    public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility) {
        return super.onAppear(sceneRoot, startValues, startVisibility, endValues, endVisibility);
    }

    @Override
    public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility) {
        return super.onDisappear(sceneRoot, startValues, startVisibility, endValues, endVisibility);
    }
}

但和继承Transition一样有传入参数的问题。

3.3 如何获取起始状态的参数?示例
3.3.1 SharedElelmentCallback

SharedElelmentCallback是共享元素动画的回调,通过下面两个方法进行设置。

activity.setExitSharedElementCallback(callback)
activity.setEnterSharedElementCallback(callback)

SharedElementCallback是一个抽象类,共有以下7个回调,所有回调都有默认实现。值得注意的是这些回调在进入和退出时的调用顺序是不一致的。

public abstract class SharedElementCallback {
    ...
    public void onSharedElementStart(List<String> sharedElementNames,
            List<View> sharedElements, List<View> sharedElementSnapshots) {}
  
    public void onSharedElementEnd(List<String> sharedElementNames,
            List<View> sharedElements, List<View> sharedElementSnapshots) {}
  
  
    /**
     * 在之前的步骤里(onMapSharedElements)被从ShareElements列表里除掉的View会在此回调,
     * 不处理的话默认进行alpha动画消失
     */
    public void onRejectSharedElements(List<View> rejectedSharedElements) {}
  
    /**
     * 最先调用,用于在动画开始前调整SharedElements。比如滑动大图过后,返回原来界面时,被选择的小图已发生改变,即共享元素已经改变,需要调整。
     */
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}

    /**
     * 在这里会把ShareElement里值得记录的信息存到为Parcelable格式,发送到Activity B
     * 默认处理规则是ImageView会特殊记录Bitmap、ScaleType、Matrix,其它View只记录大小和位置
     */
    public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,
            RectF screenBounds) {
        if (sharedElement instanceof ImageView) {
            ImageView imageView = ((ImageView) sharedElement);
            Drawable d = imageView.getDrawable();
            Drawable bg = imageView.getBackground();
            if (d != null && (bg == null || bg.getAlpha() == 0)) {
                Bitmap bitmap = TransitionUtils.createDrawableBitmap(d, imageView);
                if (bitmap != null) {
                    Bundle bundle = new Bundle();
                    if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
                        bundle.putParcelable(BUNDLE_SNAPSHOT_BITMAP, bitmap);
                    } else {
                        HardwareBuffer hardwareBuffer = bitmap.getHardwareBuffer();
                        bundle.putParcelable(BUNDLE_SNAPSHOT_HARDWARE_BUFFER, hardwareBuffer);
                        ColorSpace cs = bitmap.getColorSpace();
                        if (cs != null) {
                            bundle.putInt(BUNDLE_SNAPSHOT_COLOR_SPACE, cs.getId());
                        }
                    }
                    bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE,
                            imageView.getScaleType().toString());
                    if (imageView.getScaleType() == ScaleType.MATRIX) {
                        Matrix matrix = imageView.getImageMatrix();
                        float[] values = new float[9];
                        matrix.getValues(values);
                        bundle.putFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX, values);
                    }
                    return bundle;
                }
            }
        }
        if (mTempMatrix == null) {
            mTempMatrix = new Matrix(viewToGlobalMatrix);
        } else {
            mTempMatrix.set(viewToGlobalMatrix);
        }
        ViewGroup parent = (ViewGroup) sharedElement.getParent();
        return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds, parent);
    }

  	/**
     * 在这里会把Activity A传过来的Parcelable数据,重新生成一个View,这个View的大小和位置会与Activity A里的
     * ShareElement一致(在原来的位置和大小,上作为DecorView的overlay)。
     */
    public View onCreateSnapshotView(Context context, Parcelable snapshot) {
        View view = null;
        if (snapshot instanceof Bundle) {
            Bundle bundle = (Bundle) snapshot;
            HardwareBuffer buffer = bundle.getParcelable(BUNDLE_SNAPSHOT_HARDWARE_BUFFER);
            Bitmap bitmap = bundle.getParcelable(BUNDLE_SNAPSHOT_BITMAP);
            if (buffer == null && bitmap == null) {
                return null;
            }
            if (bitmap == null) {
                ColorSpace colorSpace = null;
                int colorSpaceId = bundle.getInt(BUNDLE_SNAPSHOT_COLOR_SPACE, 0);
                if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
                    colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
                }
                bitmap = Bitmap.wrapHardwareBuffer(buffer, colorSpace);
            }
            ImageView imageView = new ImageView(context);
            view = imageView;
            imageView.setImageBitmap(bitmap);
            imageView.setScaleType(
                    ScaleType.valueOf(bundle.getString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE)));
            if (imageView.getScaleType() == ScaleType.MATRIX) {
                float[] values = bundle.getFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX);
                Matrix matrix = new Matrix();
                matrix.setValues(values);
                imageView.setImageMatrix(matrix);
            }
        } else if (snapshot instanceof Bitmap) {
            Bitmap bitmap = (Bitmap) snapshot;
            view = new View(context);
            Resources resources = context.getResources();
            view.setBackground(new BitmapDrawable(resources, bitmap));
        }
        return view;
    }
  
 		/**
     * 表示ShareElement已经全部就位,可以开始动画了
     */
    public void onSharedElementsArrived(List<String> sharedElementNames,
            List<View> sharedElements, OnSharedElementsReadyListener listener) {
        listener.onSharedElementsReady();
    }

    /**
     * Listener to be called after {@link
     * SharedElementCallback#onSharedElementsArrived(List, List, OnSharedElementsReadyListener)}
     * when the shared elements are ready to be hidden in the source Activity and shown in the
     * destination Activity.
     */
    public interface OnSharedElementsReadyListener {

        /**
         * Call this method during or after the OnSharedElementsReadyListener has been received
         * in {@link SharedElementCallback#onSharedElementsArrived(List, List,
         * OnSharedElementsReadyListener)} to indicate that the shared elements are ready to be
         * hidden in the source and shown in the destination Activity.
         */
        void onSharedElementsReady();
    }
}

返回流程各个回调的调用顺序

ActivityB.onMapSharedElements()
->ActivityA.onMapSharedElements()
->ActivityA.onCaptureSharedElementSnapshot()
->ActivityB.onCreateSnapshotView()
->ActivityB.onSharedElementEnd()    
->ActivityB.onSharedElementStart()   //你没有看错,就是先End再Start
->ActivityB.onSharedElementsArrived()
->ActivityA.onSharedElementsArrived()
->ActivityA.onRejectSharedElements()
->ActivityA.onCreateSnapshotView()
->ActivityA.onSharedElementStart()
->ActivityA.onSharedElementEnd()
3.3.2 示例
public class SharedElementInfo implements Parcelable {
    //额外的信息
    protected transient TextView mTextView;
    private String mText;
    private Integer mTextColor;
    private Float mTextSize;
    //默认封装的信息
    private Parcelable mSnapshot;
    //是否是进入动画
    private Boolean isEnter;

    public SharedElementInfo(TextView textView){
        this.mTextView = textView;
        this.mText = textView.getText().toString();
        this.mTextColor = textView.getCurrentTextColor();
        this.mTextSize = textView.getTextSize();
        textView.setTag(this);
    }

    public void setSnapshot(Parcelable mSnapshot) {
        this.mSnapshot = mSnapshot;
    }

    public Parcelable getSnapshot(){
        return  mSnapshot;
    }

    public Boolean getIsEnter(){
        return isEnter;
    }
    public void setIsEnter(Boolean b){
        isEnter = b;
    }

    public String getText(){
        return mText;
    }

    public Integer getTextColor(){
        return mTextColor;
    }

    public Float getTextSize(){
        return mTextSize;
    }

    protected SharedElementInfo(Parcel in) {
        mText = in.readString();
        if (in.readByte() == 0) {
            mTextColor = null;
        } else {
            mTextColor = in.readInt();
        }
        if (in.readByte() == 0) {
            mTextSize = null;
        } else {
            mTextSize = in.readFloat();
        }
        mSnapshot = in.readParcelable(Parcelable.class.getClassLoader());
    }

    public static final Creator<SharedElementInfo> CREATOR = new Creator<SharedElementInfo>() {
        @Override
        public SharedElementInfo createFromParcel(Parcel in) {
            return new SharedElementInfo(in);
        }

        @Override
        public SharedElementInfo[] newArray(int size) {
            return new SharedElementInfo[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mText);
        dest.writeInt(mTextColor);
        dest.writeFloat(mTextSize);
        dest.writeParcelable(mSnapshot,flags);
    }
}

#MainActivity

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

    btn_startTransition.setOnClickListener(view->{
        //设置退出时共享元素动画的回调
        setExitSharedElementCallback(new SharedElementCallback() {
            @Override
            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
              //清空
                names.clear();
                sharedElements.clear();
              //设置我们想要的共享元素
                names.add(iv_profile.getTransitionName());
                names.add(tv_userName.getTransitionName());
                sharedElements.put(iv_profile.getTransitionName(),iv_profile);
                sharedElements.put(tv_userName.getTransitionName(),tv_userName)
                  //创建SharedElelmentInfo类并与textView绑定
                SharedElementInfo info = new SharedElementInfo(tv_userName);
            }

            @Override
            public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix, RectF screenBounds) {
              //没有绑定SharedElelmentInfo的共享元素就返回默认的snapshot
                if(sharedElement.getTag() == null){
                    return super.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, screenBounds);
                }else {
                  //有的话就在SharedElelmentInfo中存我们想获得的额外参数信息
                    SharedElementInfo info = (SharedElementInfo) sharedElement.getTag();
                    info.setSnapshot(super.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, screenBounds));
                    return info;
                }
            }
        });
        //启动共享元素动画并跳转
        ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(this,
                Pair.create(iv_profile,iv_profile.getTransitionName()),
                Pair.create(tv_userName,tv_userName.getTransitionName())
        );
        startActivity(new Intent(this, MainActivity2.class),activityOptions.toBundle());
    });
}

#MainActivity2

private void setSharedElementAnimation() {
        final AtomicBoolean isEnter = new AtomicBoolean(true);
        setEnterSharedElementCallback(new SharedElementCallback() {
            @Override
            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
                names.clear();
                sharedElements.clear();
                names.add(iv_profile.getTransitionName());
                names.add(tv_userName.getTransitionName());
                sharedElements.put(iv_profile.getTransitionName(),iv_profile);
                sharedElements.put(tv_userName.getTransitionName(),tv_userName);
            }

            @Override
            public View onCreateSnapshotView(Context context, Parcelable snapshot) {
                if (snapshot instanceof SharedElementInfo) {
                    SharedElementInfo info = (SharedElementInfo) snapshot;
                    View view = super.onCreateSnapshotView(context, info.getSnapshot());
                    view.setTag(snapshot);
                    return view;
                } else {
                    return super.onCreateSnapshotView(context, snapshot);
                }
            }

            @Override
            public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
                super.onSharedElementStart(sharedElementNames, sharedElements, sharedElementSnapshots);
                if (sharedElements != null && sharedElementSnapshots != null) {
                    for (int i = 0; i < sharedElements.size(); i++) {
                        View snapshotView = sharedElementSnapshots.get(i);
                        View shareElementView = sharedElements.get(i);
                        if(snapshotView.getTag()!=null){
                            ((SharedElementInfo)snapshotView.getTag()).setIsEnter(isEnter.get());
                            shareElementView.setTag(snapshotView.getTag());
                        }
                    }
                }
            }

            @Override
            public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
                super.onSharedElementEnd(sharedElementNames, sharedElements, sharedElementSnapshots);
                for (int i = 0; sharedElements != null && i < sharedElements.size(); i++) {
                    View snapshotView = sharedElementSnapshots.get(i);
                    View shareElementView = sharedElements.get(i);
                    if(snapshotView.getTag()!=null){
                        ((SharedElementInfo)snapshotView.getTag()).setIsEnter(isEnter.get());
                        shareElementView.setTag(snapshotView.getTag());
                    }
                }
                isEnter.set(false);
            }
        });
        TransitionSet transitionSet = new TransitionSet();
        transitionSet.addTransition(new ChangeClipBounds());
        transitionSet.addTransition(new ChangeBounds());
        //加入自定义的Transition
        transitionSet.addTransition(new ChangeTextTransition());
        transitionSet.addTransition(new ChangeImageTransform());
        transitionSet.setDuration(4000);
        getWindow().setSharedElementEnterTransition(transitionSet);
  
        //设置非共享元素动画
        getWindow().setEnterTransition(new Fade());
        getWindow().setEnterTransition(new Fade());
      
        // 防止状态栏闪烁
        Transition enterTransition = getWindow().getEnterTransition();
        Transition exitTransition = getWindow().getExitTransition();
        if (enterTransition != null) {
            enterTransition.excludeTarget(Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, true);
            enterTransition.excludeTarget(Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, true);
        }
        if (exitTransition != null) {
            exitTransition.excludeTarget(Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, true);
            exitTransition.excludeTarget(Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, true);
        }
    }

#ChangeTextTransition

public class ChangeTextTransition extends Transition {
    private static final String TAG = "ChangeTextTransition";

    private static String PROPNAME_TEXT = "xiaweizi:changeText:text";
    private static String PROPNAME_TEXT_COLOR = "xiaweizi:changeTextColor:color";
    private static String PROPNAME_TEXT_SIZE = "xiaweizi:changeTextSize:size";
    public static int TAG_KEY_TYPEFACE_LEVEL = 666;

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues,true);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues,false);
    }

    private void captureValues(TransitionValues transitionValues,Boolean start){
        if (transitionValues == null || !(transitionValues.view instanceof TextView)) return;
        if(transitionValues.view.getTag() == null) return;
        SharedElementInfo info = (SharedElementInfo) transitionValues.view.getTag();
        if((info.getIsEnter()&&start)||(!info.getIsEnter()&&!start)){
            transitionValues.values.put(PROPNAME_TEXT, info.getText());
            transitionValues.values.put(PROPNAME_TEXT_COLOR, info.getTextColor());
            transitionValues.values.put(PROPNAME_TEXT_SIZE, info.getTextSize());
        }else{
            TextView textView = ((TextView) transitionValues.view);
            transitionValues.values.put(PROPNAME_TEXT, textView.getText());
            transitionValues.values.put(PROPNAME_TEXT_COLOR, textView.getCurrentTextColor());
            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;
        }
        if (!(endValues.view instanceof TextView)) {
            return super.createAnimator(sceneRoot, startValues, endValues);
        }
        TextView textView = (TextView) endValues.view;
        CharSequence startText = (CharSequence) startValues.values.get(PROPNAME_TEXT);
        CharSequence endText = (CharSequence) endValues.values.get(PROPNAME_TEXT);
        int startTextColor = (int) startValues.values.get(PROPNAME_TEXT_COLOR);
        int endTextColor = (int) endValues.values.get(PROPNAME_TEXT_COLOR);
        float startTextSize = (float) startValues.values.get(PROPNAME_TEXT_SIZE);
        float endTextSize = (float) endValues.values.get(PROPNAME_TEXT_SIZE);

        AnimatorSet animatorSet = new AnimatorSet();
        if (!TextUtils.equals(startText, endText)) {
            animatorSet.playTogether(createTextChangeAnimator(textView, startText, endText));
        }
        if (startTextColor != endTextColor) {
            animatorSet.playTogether(createColorChangeAnimator(textView, startTextColor, endTextColor));
        }
        if (startTextSize != endTextSize) {
            animatorSet.playTogether(createSizeChangeAnimator(textView, startTextSize, endTextSize));
        }
        return animatorSet;
    }

    private Animator createColorChangeAnimator(final TextView endView, final int startTextColor, final int endTextColor) {
        ObjectAnimator animator = ObjectAnimator.ofArgb(endView, new TextColorProperty(), startTextColor, endTextColor);
        animator.setDuration(2000);
        return animator;
    }

    private Animator createSizeChangeAnimator(final TextView endView, final float startTextSize, final float endTextSize) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(endView, new TextSizeProperty(), startTextSize, endTextSize);
        animator.setDuration(2000);
        return animator;
    }

    private Animator createTextChangeAnimator(TextView textView, CharSequence startText, CharSequence endText) {
        ValueAnimator animator =  ValueAnimator.ofFloat(0f,1f);
        animator.setDuration(2000);
        animator.addUpdateListener(animation -> {
            Float animatedValue = (Float)animation.getAnimatedValue();
            if(animatedValue<0.5f){
//                textView.setText(startText);
                textView.setAlpha((0.5f-animatedValue)*2);
            }else{
//                textView.setText(endText);
                textView.setAlpha((animatedValue-0.5f)*2);
            }
        });
        return animator;
    }

    private class TextColorProperty extends Property<TextView, Integer> {

        TextColorProperty() {
            super(Integer.class, "textColor");
        }

        @Override
        public void set(TextView object, Integer value) {
            object.setTextColor(value);
        }

        @Override
        public Integer get(TextView object) {
            return object.getCurrentTextColor();
        }
    }

    private class TextSizeProperty extends Property<TextView, Float> {

        TextSizeProperty() {
            super(Float.class, "textSize");
        }

        @Override
        public void set(TextView object, Float value) {
            object.setTextSize(TypedValue.COMPLEX_UNIT_PX, value);
        }

        @Override
        public Float get(TextView object) {
            return object.getTextSize();
        }
    }
}