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

文章详细介绍了如何在Android中使用Transition类创建自定义动画,包括位置、旋转、缩放和颜色变化,以及如何结合Scene和TransitionSet实现流畅的视图切换。还分享了不同类型的过渡动画案例和面试复习路线,提供完整的开发学习资源列表。
摘要由CSDN通过智能技术生成

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:

@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;

}

最终效果:

ChangeImageResourceTransition

有的时候发现,在切换图片的时候过度会很生硬,那可以通过在对 View 的 alpha 属性从 101 的过程中替换图片,这样显得很平滑。

@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;

最终效果:

ChangeCustomTransition

除了 View 原生的属性,自定义属性同样也可以。创建 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;

}

主要在自定义 View 的绘制逻辑:

@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();

}

最终的效果:

Scene


终于开始介绍文章开头的效果是如何实现的:

有了前面的基础铺垫,实现起来就很简单。

Scene 就是为这种场景的过度而设计,不需要关注过度过程,只需要传入前后的布局,并保证各个元素的 id 保持一致即可。

  1. 创建前后 layoutlayout_scene1.xmllayout_scene2.xml 具体代码就补贴了

  2. 创建前后 Scene 对象;

mScene1 = Scene.getSceneForLayout(mRoot, R.layout.layout_scene1, this);

mScene2 = Scene.getSceneForLayout(mRoot, R.layout.layout_scene2, this);

  1. 创建转场 Transition;我们把之前自定的组合成 TransitionSet

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());

}

}

  1. 开始切换场景;

TransitionManager.go(mScene1, mTransition);

TransitionManager.go(mScene2, mTransition);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

面试复习路线,梳理知识,提升储备

自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。

关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

  • 架构师筑基必备技能
  • Android高级UI与FrameWork源码
  • 360°全方面性能调优
  • 解读开源框架设计思想
  • NDK模块开发
  • 微信小程序
  • Hybrid 开发与Flutter

知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

Android开发七大模块核心知识笔记

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

点进行了完美的总结:

[外链图片转存中…(img-K7P7zEDb-1712824908669)]

《960全网最全Android开发笔记》

[外链图片转存中…(img-TlpHbSj6-1712824908669)]

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

[外链图片转存中…(img-6i02GJFj-1712824908670)]

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值