PanelSwitchHelper 框架已经有一段时间了,功能的稳定性,系统版本及机型兼容性也没问题。随着业界的交互做得越来越好,issue 上有朋友建议能够增强框架的交互,在切换面板/输入法的时候能更为平滑地过渡。
大家还希望 PanelSwitchHelper 新增哪些功能?欢迎大家盖楼 issue可见建议。
在 version 1.1.0 之前,PanewitchHelper 的实现大致为:
- 通过监听 window 变化计算输入法的高度;
- 设置框架根布局为线型布局且动态更改 weight 值,在输入法隐藏和显示前夕, weight 设置为1;切换功能面本是 weigth 设置为0且高度固定。
由于 weight 的值是线型布局测量时使用 weightSum 进行等分计算,window 变化之后,线型布局自然重新计算绘制,每一次 layou 布局时会获取当前系统测量后的具体值。切换输入法时,layout 时机必定在 window 变化之后,所以 ContentContainer 的变化肯定稍慢于 window 的变化,仔细观看会发现有些卡顿。切换面板时,由于上层 ContentContainer 高度固定,所以 PanelContainer 区域就会可见,这样切换虽然界面不会跳动,但是会生硬。
尝试过使用属性动画来动态更改 ContentContainer 的高度,但是 PanelContainer 切换依然会卡顿。原因是 “我试图从 contentContainer 和 panelContainer 独立的部分” 进行动画过度。后面发现从根本上思路错了。要实现 “微信切换面板” 的效果,应该把两者看成一个整体。
“你可以看到,微信在切换输入法的时候很平滑。”
“你可以看到,微信在切换功能面板的时候,好像是面板一开始就已经在屏幕底部下面,然后被推上去一样。”
所以在显示和隐藏的时候,直接干预 ContentContainer 和 PanelContainer 父布局 P
anelSwitchLayout 的绘制。假设这里我们暂时忽略状态栏和导航栏,且 PanelSwitchLayout 铺满界面场景,H(view) 表示 view 的高度
- 界面的高度 H(panelSwitchLayout)
- contentContainer 的高度 H(contentContainer)
- panelContainer 的高度 H(panelContainer)
- 输入法高度 H(Keyboard)
框架需要考虑的场景都 3 个
- 输入法隐藏面板隐藏
- 输入法隐藏面板显示
- 输入法显示面板显示
且满足
- H(panelSwitchLayout) = H(contentContainer)
- H(panelContainer) = H(Keyboard)
这里我们要利用 ChangeBounds 的动画实现来帮助 PanelSwitchLayout 在 Bound 变化的时候做动画过度,那么我们就得从 3 个场景下完成正确的边缘计算。满足如下:“panelSwitchLayout 在 onLayout 的时候,需要从上往下一次 layout contentContainer 和 panelContainer。” view.top 表示 view 在其 parent 内 layout时的 top 值。
- 输入法隐藏面板隐藏,contentContainer.top = 0
- 输入法隐藏面板显示,contentContainer.top = -H(panelContainer)
- 输入法显示面板隐藏,contentContainer.top = -H(panelContainer)
“那么,输入法隐藏面板隐藏场景下,我们就能看到整个界面都是 ContentContainer,面板/输入法显示的时候,整体上移,非常连贯。”
但实际上我们还需要计算状态栏,导航栏,标题栏等视图元素,还需要考虑 PanelSwitchLayout 不是当前界面的根布局场景。这部分计算非常痛苦,要考虑的东西非常多。 同时还要考虑界面是否支持沉浸,用户机型会不会存在动态隐藏导航等等(此时心里有百万个草泥马蹦腾)。尝试了一整天,每一种方案都可能在另一些特殊场景中不适用。
在隔天早上去公司的路上,感觉有了能兼容所有场景的灵感,可结合以下条件综合计算。
- 计算除了导航栏高度之外的界面所有高度 h1;
- 计算 panelSwitchLayout 在界面的绝对 y 值(也就是绘制起点距离顶部的高度);
- h1 - y - panelSwitchLayout.paddingTop 可以得到 panelSwitchLayout 该有的高度 h2;
- 如果设备有导航栏且不可见,则 h2 需要加上导航栏高度。
最后计算的代码逻辑如下。
1 | /** |
感兴趣可以参考 PanelSwitchLayout 类实现。
路漫漫其修远兮,只要有心实现就一定可以!