当调用 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
的原理很简单,大致的逻辑如下:
- 记录当前状态的属性值,比如位置大小或者自定义属性之类
- 创建执行动画,参数为当前值和目标值,根据对应算法来完成动画效果
- 根据目标状态的属性值和记录的缓存属性值,调用创建好的动画对象执行即可
那落实到代码中,首先先集成 Transition
类,会让你实现三个方法:captureStartValues
、captureEndValues
和createAnimator
。
- 定义你关心的属性值;
官方建议属性定义的规则为:package_name:transition_class:property_name
.
比如
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)
复制代码
参数值的 startValues
和endValues
分别可以拿到你存储的属性值,之后创建动画并返回即可,后续系统会根据你创建的动画进行转场。
是不是很简单,接下来通过几个案例带大家感受一下:
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
保持一致即可。
- 创建前后
layout
;layout_scene1.xml
和layout_scene2.xml
具体代码就补贴了 - 创建前后
Scene
对象;
mScene1 = Scene.getSceneForLayout(mRoot, R.layout.layout_scene1, this);
mScene2 = Scene.getSceneForLayout(mRoot, R.layout.layout_scene2, this);
面试复习路线,梳理知识,提升储备
自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。
关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)
- 架构师筑基必备技能
- Android高级UI与FrameWork源码
- 360°全方面性能调优
- 解读开源框架设计思想
- NDK模块开发
- 微信小程序
- Hybrid 开发与Flutter
知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:
《960全网最全Android开发笔记》
《379页Android开发面试宝典》
历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数
[外链图片转存中…(img-8oOtSOQ9-1715154625950)]
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!