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对象。
方法名 | 说明 |
| 以一个点为中心开始揭露 |
| 类似 |
| 放大一个 |
| 以一个点为中心开始放大 |
** | 无共享元素过渡动画 |
** | 单个共享元素的过渡动画 |
** | 多个共享元素的过渡动画 |
最常用的是最后三个方法,这三个方法会设置好前后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引入了 Transition
。Transition
被用在两个场景之间的UI变换,它会捕捉开始场景和结束场景中每个 View 的状态,然后创建Animator来完成两个场景之间的变换。
2.3 设置非共享的动画效果
也叫设置内容过渡动画,其实就是设置除了共享元素以外的元素的动画效果。
#Window.java
方法名 | 说明 |
| 可以理解成创建Activity时非共享元素的入场动画 |
| 可以理解成销毁Activity时非共享元素的退场动画 |
| 可以理解成Activity从后台回到前台时非共享元素的入场动画 |
| 可以理解成Activity从前台退到后台时非共享元素的退场动画 |
2.4 设置共享元素的动画效果
#Window.java
方法名 | 说明 |
| 可以理解成创建Activity时共享元素的入场动画 |
| 可以理解成销毁Activity时共享元素的退场动画 |
| 可以理解成Activity从后台回到前台时共享元素的入场动画 |
| 可以理解成Activity从前台退到后台时共享元素的退场动画 |
2.5 系统自带的Transition
以下 Transition
通常被用来设置共享元素的动画效果。
因为共享元素是一个从有到有的过程,而非共享元素是从无到有或者从有到无的过程,因此系统提供的Transition是不一样的。
类名 | 说明 |
| 在变换中更改布局边界,即改变View的大小和位置 |
| 在变换中更改 |
| 在变换中更改 |
| 在变换中更改 |
| 在变换中更改 |
2.6 系统自带的Visibility
Visibility
继承了 Transition
,因此系统自带的 Visibility
也属于系统自带的 Transition
,以下这几个 Transition
通常用在设置非共享元素的动画效果。
类名 | 说明 |
| 分解/聚合动画,如果没有提供震中,会从焦点View和View的中点移入/移出。 |
| 淡入/淡出动画 |
| 滑入/滑出动画 |
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类似,Visibility
比 Transition
多提供了 onAppear
和 onDisappear
方法。我们可以在 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();
}
}
}