舒服了~玩转Android转场动画!
Posted 刘望舒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了舒服了~玩转Android转场动画!相关的知识,希望对你有一定的参考价值。
者:下位子
https://juejin.cn/post/6880409898363027463
头像,位置和大小以及 scaleType 发生变化
背景,颜色、位置和大小发生变化
名称,字体大小、颜色和位置发生变化
描述,字体大小和位置发生变化
直接把上述的每个对象看做是独立个体,各自创建独立的动画对象,控制其执行和结束状态。
这种方式,无疑是最简单粗暴的,但是实现和维护起来都很困难,更不容易拓展
使用MotionLayout,不得不说很强大,是Google推崇的动画组件,基本不用编写 java 代码就可完成负责的手势和动画,后面有时间会介绍。
使用Transition,Google在android 5.0完整引入,虽没有MotionLayout那么强大,但是其复用性很强,并且很容易理解,上手也很快。
原生提供的Transition类
自己实现Transition类
Scene
原生 Transition
private void init() {
setOrdering(ORDERING_SEQUENTIAL);
addTransition(new Fade(Fade.OUT)).
addTransition(new ChangeBounds()).
addTransition(new Fade(Fade.IN));
}
Fade fade = new Fade();
Slide slide = new Slide();
TransitionSet set = new TransitionSet();
set.addTransition(fade).addTransition(slide).setOrdering(TransitionSet.ORDERING_TOGETHER);
ChangeBounds transition = new ChangeBounds();
transition.setInterpolator(new AnticipateInterpolator());
TransitionManager.beginDelayedTransition(mRoot, transition);
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) view3.getLayoutParams();
if (layoutParams.leftMargin == 400) {
layoutParams.leftMargin = 50;
} else {
layoutParams.leftMargin = 400;
}
view3.setLayoutParams(layoutParams);
ChangeClipBounds transition = new ChangeClipBounds();
transition.setInterpolator(new BounceInterpolator());
TransitionManager.beginDelayedTransition(mRoot, transition);
int width = view2.getWidth();
int height = view2.getHeight();
int gap = 140;
Rect rect = new Rect(0, gap, width, height - gap);
if (rect.equals(view2.getClipBounds())) {
view2.setClipBounds(null);
} else {
view2.setClipBounds(rect);
}
ChangeScroll transition = new ChangeScroll();
transition.setInterpolator(new AnticipateOvershootInterpolator());
TransitionManager.beginDelayedTransition(mRoot, transition);
if (view1.getScrollX() == -100 && view1.getScrollY() == -100) {
view1.scrollTo(0, 0);
} else {
view1.scrollTo(-100, -100);
}
ChangeTransform transition = new ChangeTransform();
transition.setInterpolator(new OvershootInterpolator());
TransitionManager.beginDelayedTransition(mRoot, transition);
if (view1.getTranslationX() == 100 && view1.getTranslationY() == 100) {
view1.setTranslationX(0);
view1.setTranslationY(0);
} else {
view1.setTranslationX(100);
view1.setTranslationY(100);
}
if (view2.getRotationX() == 30f) {
view2.setRotationX(0);
} else {
view2.setRotationX(30);
}
if (view3.getRotationY() == 30f) {
view3.setRotationY(0);
} else {
view3.setRotationY(30);
}
if (view4.getScaleX() == 0.5f && view4.getScaleY() == 0.5f) {
view4.setScaleX(1f);
view4.setScaleY(1f);
} else {
view4.setScaleX(0.5f);
view4.setScaleY(0.5f);
}
自定义 Transition
记录当前状态的属性值,比如位置大小或者自定义属性之类
创建执行动画,参数为当前值和目标值,根据对应算法来完成动画效果
根据目标状态的属性值和记录的缓存属性值,调用创建好的动画对象执行即可
private static String PROPNAME_TEXT_COLOR = "xiaweizi:changeTextColor:color";
void captureStartValues(TransitionValues transitionValues)
void captureEndValues(TransitionValues transitionValues);
transitionValues.values.put(PROPNAME_TEXT_COLOR, view.getCurrentTextColor());
Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues)
https://github.com/xiaweizi/TransitionDemo/blob/master/app/src/main/java/com/xiaweizi/transitiondemo/transition/ChangeTextTransition.java
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";
private static String PROPNAME_TEXT_LEVEL = "xiaweizi:changeTextTypeface:level";
// 记录下起始状态属性值
private void captureValues(TransitionValues transitionValues) {
if (transitionValues == null || !(transitionValues.view instanceof TextView)) return;
TextView view = (TextView) transitionValues.view;
transitionValues.values.put(PROPNAME_TEXT, view.getText());
transitionValues.values.put(PROPNAME_TEXT_COLOR, view.getCurrentTextColor());
transitionValues.values.put(PROPNAME_TEXT_SIZE, view.getTextSize());
transitionValues.values.put(PROPNAME_TEXT_LEVEL, view.getTag(R.id.type_face_level));
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
if (!(endValues.view instanceof TextView)) {
return super.createAnimator(sceneRoot, startValues, endValues);
}
TextView endView = (TextView) endValues.view;
int startTextColor = (int) startValues.values.get(PROPNAME_TEXT_COLOR);
int endTextColor = (int) endValues.values.get(PROPNAME_TEXT_COLOR);
ObjectAnimator animator = ObjectAnimator.ofArgb(endView, new TextColorProperty(), startTextColor, endTextColor);
animator.setDuration(300);
return animator;
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
final View endView = endValues.view;
ColorDrawable startColorDrawable = (ColorDrawable) startValues.values.get(PROPNAME_COLOR);
ColorDrawable endColorDrawable = (ColorDrawable) endValues.values.get(PROPNAME_COLOR);
if (startColorDrawable == null || endColorDrawable == null) return super.createAnimator(sceneRoot, startValues, endValues);
final int startColor = startColorDrawable.getColor();
final int endColor = endColorDrawable.getColor();
ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor, endColor);
animator.setDuration(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
endView.setBackgroundColor(animatedValue);
}
});
return animator;
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
if (!(endValues.view instanceof ImageView)) {
return super.createAnimator(sceneRoot, startValues, endValues);
}
final ImageView endView = (ImageView) endValues.view;
final Drawable startDrawable = (Drawable) startValues.values.get(PROPNAME_IMAGE_RESOURCE);
final Drawable endDrawable = (Drawable) endValues.values.get(PROPNAME_IMAGE_RESOURCE);
ValueAnimator animator = ValueAnimator.ofFloat(0, 1f);
animator.setDuration(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
if (animatedValue <= 0.5f) {
endView.setImageDrawable(startDrawable);
float ratio = (0.5f - animatedValue) / 0.5f;
endView.setAlpha(ratio);
} else {
endView.setImageDrawable(endDrawable);
float ratio = (animatedValue - 0.5f) / 0.5f;
endView.setAlpha(ratio);
}
}
});
return animator;
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
if (!(endValues.view instanceof TransitionView)) {
return super.createAnimator(sceneRoot, startValues, endValues);
}
final TransitionView endView = (TransitionView) endValues.view;
final float startRatio = (float) startValues.values.get(PROPNAME_CUSTOM_RATIO);
final float endRatio = (float) endValues.values.get(PROPNAME_CUSTOM_RATIO);
ObjectAnimator animator = ObjectAnimator.ofFloat(endView, "ratio", startRatio, endRatio);
animator.setDuration(300);
return animator;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制左边
canvas.save();
mRect.set(0, 0, (int) (getWidth() * mRatio), getHeight());
canvas.clipRect(mRect);
mTextPaint.setColor(mStartColor);
TransitionUtils.drawTextCenter(canvas, "文本三", getWidth() / 2, getHeight() / 2, mTextPaint);
canvas.restore();
// 绘制右边
canvas.save();
mRect.set((int) (getWidth() * mRatio), 0, getWidth(), getHeight());
canvas.clipRect(mRect);
mTextPaint.setColor(mEndColor);
TransitionUtils.drawTextCenter(canvas, "三本文", getWidth() / 2, getHeight() / 2, mTextPaint);
canvas.restore();
}
mScene1 = Scene.getSceneForLayout(mRoot, R.layout.layout_scene1, this);
mScene2 = Scene.getSceneForLayout(mRoot, R.layout.layout_scene2, this);
public class SceneTransition extends TransitionSet {
public SceneTransition() {
init();
}
public SceneTransition(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
addTransition(new ChangeTextTransition())
.addTransition(new ChangeScroll())
.addTransition(new ChangeBackgroundColorTransition())
.addTransition(new ChangeBounds());
}
}
TransitionManager.go(mScene1, mTransition);
TransitionManager.go(mScene2, mTransition);
总结
推荐阅读
•
•
•
·················END·················
你好,我是刘望舒,腾讯云TVP国内Android领域第一人,著有畅销书《Android进阶之光》《Android进阶解密》《Android进阶指北》,电子工业出版社2017、2018、2019、2020年度优秀作者。
前华为面试官,现大厂技术负责人。
欢迎添加我的微信 henglimogan ,备注:BATcoder,加入BATcoder交流群。
明天见(。・ω・。)
以上是关于舒服了~玩转Android转场动画!的主要内容,如果未能解决你的问题,请参考以下文章
android Activity转场动画makeSceneTransitionAnimation
Android中的转场动画以及material-components-android 使用
Android中的转场动画以及material-components-android 使用
android转场动画windowAnimation和ActivityAnimation的区别