『Android 技能篇』优雅的转场动画之 Transition

相信大家在平常开发也会遇到类似的转场动画,如果想要要实现上图的效果有哪些方式呢?首先分析一下转场过程,我们把起始View分别定义为startViewendViewstartView 为常见的列表布局,左侧头像和右侧为文本介绍;endView 为详情页面,置顶的大图和详细的文本介绍。不难发现,这些元素都是对应关系,只不过起始状态的基本属性不同:

  • 头像,位置和大小以及 scaleType 发生变化

  • 背景,颜色、位置和大小发生变化

  • 名称,字体大小、颜色和位置发生变化

  • 描述,字体大小和位置发生变化

对于此效果,有很多办法可以实现,综合其实现成本和预期效果进行最终选择,我能想到的大概有三种:

1、直接把上述的每个对象看做是独立个体,各自创建独立的动画对象,控制其执行和结束状态。这种方式,无疑是最简单粗暴的,但是实现和维护起来都很困难,更不容易拓展

2、使用 MotionLayout,不得不说很强大,是 Google 推崇的动画组件,基本不用编写 java 代码就可完成负责的手势和动画,后面有时间会介绍。

3、使用 Transition,Google 在 Android 5.0 完整引入,虽没有 MotionLayout 那么强大,但是其复用性很强,并且很容易理解,上手也很快。

今天咱们就以下面三个方向并结合对应效果来带大家了解一下 Transition。

  1. 原生提供的 Transition 类

  2. 自己实现 Transition 类

  3. Scene

原生 Transition



准备

核心关键类 TransitionManager, TransitionManager.beginDelayedTransition(ViewGroup viewGroup, Transition transition); 作为动画的开始,传入需要做转场动画的父布局或根布局,随后改变 View 的相关属性,比如 setVisible(),便可自动完成转场动画效果。

默认实现的 AutoTransition,内部集成了基础动画:

private void init() {

setOrdering(ORDERING_SEQUENTIAL);

addTransition(new Fade(Fade.OUT)).

addTransition(new ChangeBounds()).

addTransition(new Fade(Fade.IN));

}

Slide、Fade 和 Explode

这三者作为 Visibility 的三个子类,通过控制 view.setVisible() 的方式来达到具体的效果。

Fade,淡出出场,淡入入场

Slide,向下离开屏幕出场,向上进入屏幕入场

Explode,四边散开出场,四边汇入入场

同样,可以通过:

Fade fade = new Fade();

Slide slide = new Slide();

TransitionSet set = new TransitionSet();

set.addTransition(fade).addTransition(slide).setOrdering(TransitionSet.ORDERING_TOGETHER);

达到组合的效果:

ChangeBounds

此处开始同一个页面场景的切换,ChangeBounds 当View的位置或者大小发生变化时触发对应的转场效果。比如:

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

当调用 view.setClipBounds() 时会触发转场效果:

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

当调用 view.scrollTo() 会触发转场效果:

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

这个就厉害了,View 的 translation、scale 和 rotation 发生改变时都会触发:

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


介绍

其实 Transition 的原理很简单,大致的逻辑如下:

1、记录当前状态的属性值,比如位置大小或者自定义属性之类

2、创建执行动画,参数为当前值和目标值,根据对应算法来完成动画效果

3、根据目标状态的属性值和记录的缓存属性值,调用创建好的动画对象执行即可

那落实到代码中,首先先集成 Transition 类,会让你实现三个方法:captureStartValuescaptureEndValuescreateAnimator

  1. 定义你关心的属性值;官方建议属性定义的规则为:·package_name:transition_class:property_name.· 比如

private static String PROPNAME_TEXT_COLOR = “xiaweizi:changeTextColor:color”;

我想在文本颜色发生改变时做转场动画,就可以定义上述的属性。

  1. 记录起始状态的属性;

void captureStartValues(TransitionValues transitionValues)

void captureEndValues(TransitionValues transitionValues);

上述方法分别存储起始状态下对应的属性值:

transitionValues.values.put(PROPNAME_TEXT_COLOR, view.getCurrentTextColor());

  1. 创建动画;

Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues)

参数值的 startValuesendValues分别可以拿到你存储的属性值,之后创建动画并返回即可,后续系统会根据你创建的动画进行转场。

是不是很简单,接下来通过几个案例带大家感受一下:

ChangeTextTransition

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;

}

看一下这四种属性发生变化时的效果:

ChangeBackgroundColorTransition

类似于文本颜色,只不过针对的是 view.setBackground(),主要的代码在于创建 Animator:

最后

现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!

Android架构师之路很漫长,一起共勉吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A: 可以使用共享元素转场动画实现这种效果。具体步骤如下: 1. 在两个 Activity 中选定相同的 View 作为共享元素,设置相同的 transitionName。 ```xml <!--activity1--> <ImageView android:id="@+id/shared_element" android:transitionName="shared_element"/> <!--activity2--> <ImageView android:id="@+id/shared_element" android:transitionName="shared_element"/> ``` 2. 在启动 Activity2 时,设置共享元素的转场动画。 ```kotlin // activity1 val intent = Intent(this, Activity2::class.java) // 设置共享元素的转场动画 val options = ActivityOptions.makeSceneTransitionAnimation(this, sharedElement, "shared_element") startActivity(intent, options.toBundle()) ``` ```xml // activity2 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity2) // 设置共享元素的转场动画 ViewCompat.setTransitionName(sharedElement, "shared_element") // 延迟进入动画,等待布局完成 window.decorView.postDelayed({ startEnterAnimation() }, 100) } private fun startEnterAnimation() { val enterAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_in_from_right_bottom) enterAnimation.duration = 500 sharedElement.startAnimation(enterAnimation) } ``` 3. 在 Activity2 中,在退出时设置返回动画,并在动画结束后调用 `finish()` 方法。 ```kotlin override fun onBackPressed() { // 设置返回动画 val exitAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_out_to_left_top) exitAnimation.duration = 500 sharedElement.startAnimation(exitAnimation) // 在动画结束后调用 finish() 方法 exitAnimation.setAnimationListener(object : Animation.AnimationListener { override fun onAnimationStart(animation: Animation?) {} override fun onAnimationEnd(animation: Animation?) { finishAfterTransition() } override fun onAnimationRepeat(animation: Animation?) {} }) } ``` 对应的动画资源文件 `translate_in_from_right_bottom.xml` 和 `translate_out_to_left_top.xml` 可以自己定义,这里提供一组示例: ```xml <!--translate_in_from_right_bottom.xml--> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="500" android:fromXDelta="100%p" android:fromYDelta="100%p"/> <!--translate_out_to_left_top.xml--> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="500" android:toXDelta="-100%p" android:toYDelta="-100%p"/> ``` 运行效果如下: ![](https://img-blog.csdnimg.cn/2022011319273689.gif#pic_center)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值