// 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源 如果 to
路由是在屏幕外的,
// 那么我们暗中将其动画值恢复到它“移到”屏幕外之前的状态。
to.offstage = false;
for (Object tag in fromHeroes.keys) {
if (toHeroes[tag] != null) {
final _HeroFlightManifest manifest = new _HeroFlightManifest(
type: flightType,
overlay: navigator.overlay,
navigatorRect: navigatorRect,
fromRoute: from,
toRoute: to,
fromHero: fromHeroes[tag],
toHero: toHeroes[tag],
createRectTween: createRectTween,
);
if (_flights[tag] != null)
_flights[tag].divert(manifest);
else
_flights[tag] = new _HeroFlight(_handleFlightEnded)…start(manifest);
} else if (_flights[tag] != null) {
_flights[tag].abort();
}
}
}
[heroes.dart](()
这会响应路由压入/弹出事件而被调用。在第 14 行和第 15 行,你可以看到 _allHeroesFor
调用,它可以在两个页面上找到所有 Hero。从第 21 行开始构建 _HeroFlightManifest
并启动旅程。从这里开始,有一堆动画的代码设置和边缘情况的处理。我建议你看一下[整个类]((),这很有意思,里面还有很多值得学习的东西。你也可以看一下[这个](()。
Villains 是如何运行的
Villains 要比 Hero 更简单。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xrJtKqZN-1650444284705)(https://user-gold-cdn.xitu.io/2018/9/4/165a47b6d50cedcd?imageslim)]
Hero 和 3 个 Villain 使用(AppBar,Text,FAB)。
他们使用相同的机制来查找给定上下文的所有 Villain,他们还使用 NavigationObserver
自动对页面转换做出反应。但不是从一个屏幕到另一个屏幕的动画,而是仅在它们各自的屏幕上做的动画。
SequenceAnimation 和 自定义 TickerProvider
处理动画时,通常使用 SingleTickerProviderStateMixin
或 TickerProviderStateMixin
。在这种情况下,动画不会在 StatefulWidget
中启动,因此我们需要另一种方法来访问 TickerProvider
。
class TransitionTickerProvider implements TickerProvider {
final bool enabled;
TransitionTickerProvider(this.enabled);
@override
Ticker createTicker(TickerCallback onTick) {
return new Ticker(onTick, debugLabel: ‘created by $this’)…muted = !this.enabled;
}
}
自定义一个 ticker 非常简单。所有这一切都是为了实现 TickerProvider
接口并返回一个新的 Ticker
。
static Future playAllVillains(BuildContext context, {bool entrance = true}) {
List<_VillainState> villains = VillainController._allVillainssFor(context)
…removeWhere((villain) {
if (entrance) {
return !villain.widget.animateEntrance;
} else {
return !villain.widget.animateExit;
}
});
// 用于新页面动画的控制器,因为它的时间比实际页面转换更长
AnimationController controller = new AnimationController(vsync: TransitionTickerProvider(TickerMode.of(context)));
SequenceAnimationBuilder builder = new SequenceAnimationBuilder();
for (_VillainState villain in villains) {
builder.addAnimatable(
anim: Tween(begin: 0.0, end: 1.0),
from: villain.widget.villainAnimation.from,
to: villain.widget.villainAnimation.to,
tag: villain.hashCode,
);
}
SequenceAnimation sequenceAnimation = builder.animate(controller);
for (_VillainState villain in villains) {
villain.startAnimation(sequenceAnimation[villain.hashCode]);
}
/ Android开源项目《ali1024.coding.net/public/P7/Android/git》 /开始动画
return controller.forward().then((_) {
controller.dispose();
});
}
首先,所有不应该展示的 Villain(那些将 animateExit/animateEntrance 设置为 false 的人)都会被过滤掉。然后创建一个带有自定义 TickerProvider
的 AnimationController
。使用 [SequenceAnimation](() 库,每个 Villain
被分配一个动画,它们在各自的时间中运行 0.0 —— 1.0(from
和 to
持续时间)。最后,动画全部开始。当它们全部完成时,控制器被丢弃。
Villains 的 build() 方法
@override
Widget build(BuildContext context) {
Widget animatedWidget = widget.villainAnimation
.animatedWidgetBuilder(widget.villainAnimation.animatable.chain(CurveTween(curve: widget.villainAnimation.curve)).animate(_animation), widget.child);
if (widget.secondaryVillainAnimation != null) {
animatedWidget = widget.secondaryVillainAnimation.animatedWidgetBuilder(
widget.secondaryVillainAnimation.animatable.chain(CurveTween(curve: widget.secondaryVillainAnimation.curve)).animate(_animation), animatedWidget);
}
return animatedWidget;
}
这可能看起来很可怕,但请先忍耐一下。让我们看看第 3 行和第 4 行。widget.villainAnimation.animatedWidgetBuilder
是一个自定义的 typedef:
typedef Widget AnimatedWidgetBuilder(Animation animation, Widget child);
它的工作是返回一个根据动画绘制的组件(大多数时候返回的组件是一个 AnimatedWidget
)。
它得到了 Villain 的 child 和这个动画:
widget.villainAnimation.animatable.chain(CurveTween(curve: widget.villainAnimation.curve)).animate(_animation)
链方法首先评估 CurveTween
。然后它使用该值来评估调用它的 animatable
。这只是将所需的曲线添加到动画中。
这是关于 Villain 如何工作的粗略概述,请务必也查看[源代码](()并大胆地提出你们的问题。
可变的静态变量很槽糕,让我解释一下
深夜,我坐在我的办公桌前,写下测试。几个小时后,每一次单独的测试都过去了,似乎没有 bug。就在睡觉之前,我把所有的测试都放在一起,以确保它真的没问题。然后发生了这个:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkc7IGGx-1650444284706)(https://user-gold-cdn.xitu.io/2018/9/4/165a47b6ce9031f7?imageView2/0/w/1280/h/960/ignore-error/1)]
每个测试都只能单独通过。
我很困惑。每次测试都成功。果然,当我自己运行这两个测试时,它们很正常。但是当一起运行所有测试时,最后两个失败了。WTF。
第一反应显然是:“我的代码肯定没错,它一定对测试的执行方式做了些什么!也许测试是并行播放因此相互干扰?也许是因为我使用了相同的键?”
Brian Egan 向我指出,删除一个特定的测试修复了错误并将其移到顶部使得其他所有测试也失败了。如果那不是“共享数据”那么我不知道是什么。
当我发现问题是什么时,我忍不住笑了。这正是在某些情况下使用静态变量不好的原因。
基本上,预定义的动画都是静态的。我懒得为每个动画编写一个方法来获取 VillainAnimation
所需的所有参数。所以我使 VillainAnimation
是可变的(坏主意)。这样我就没有必要在方法中明确写出所有必要的参数。使用时看起来像这样:
Villain(
villainAnimation: VillainAnimation.fromBottom(0.4)
…to = Duration(milliseconds: 150),
child: Text(“HI”),
)
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上我搜集整理的2019-2021BATJ 面试真题解析,我把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
《960全网最全Android开发笔记》
《379页Android开发面试宝典》
历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析
[外链图片转存中…(img-4EL36EjZ-1650444284708)]
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析
[外链图片转存中…(img-zd2hqE64-1650444284708)]