第 08 章:Android —— ViewPager2、RecyclerView 与手势转发
1. Android 的页面骨架
HomeContainerView 继承 SwipeRefreshLayout,内部是一个 FrameLayout,再放入三类 child:
HomeContainerView : SwipeRefreshLayout
└── contentContainer : FrameLayout
├── pager : ViewPager2
│ └── HomePageView x N
│ └── RecyclerView
├── headerView : HomeHeaderView
└── tabsView : HomeTabsView初始化时 pager.isSaveEnabled = false。源码注释给出了原因:Store snapshot 才是 selected Home tab 的唯一 owner,不能让 ViewPager2 自己恢复一份可能对应旧 tab 的 RecyclerView holder。
2. State 更新的线程模型
首个 state 走同步路径,以保证首个 ViewRoot traversal 看到完整 header/pager 数据;后续 JSON 在 parser executor 解码,避免大 sections 阻塞 UI。
3. ViewPager2 的 pending transition
ViewPager2 的 onPageSelected 早于 RecyclerView 物理 settle。代码因此维护:
pendingPagerTabIdpendingPagerSelectionIsProgrammaticpendingPagerSelectionShouldNotifypagerScrollState
只有 pager 和 inner RecyclerView 都 idle,且 current item 与 pending target 一致时,才完成选择、同步 collapse offset 并发出 tab intent。Store snapshot 在过渡中到达时,不会把 pager 强行停在半页。
4. 共享 header 的垂直手势
Android 的 header/tabs 在 pager 之外,但页面 body 在各自 RecyclerView 内。用户从 shared chrome 向上滑时,HomeContainerView.onInterceptTouchEvent 会:
- 判断触点是否在 header/tabs;
- 计算 dx/dy 与 touch slop;
- 区分横向 tab 手势和纵向折叠手势;
- 对纵向手势,将 MotionEvent 坐标转换后转发给当前 page 的 RecyclerView;
- 让 inner list 负责实际滚动,并通过 collapse offset 回调更新 header/tabs 的位置。
5. RecyclerView 初始 layout commit
HomePageView 维护 hasPendingListLayoutCommit 和 hasCommittedInitialRows。原因是 React Native 拥有外层 bounds,nested requestLayout 不一定触发另一次 ViewRoot traversal。源码会在安全时机对 RecyclerView 使用同样的 exact bounds 执行 measure/layout,确认首批 rows 已提交后,才把 collapse 状态视为可靠。
这不是一般列表性能优化,而是为了保证:
- 首次缓存 rows 不会在 header 折叠前瞬间跳位;
- slot host 的 width/height 可被 Surface 正确读取;
- tab 切换时目标页可以复用当前 collapse offset。
6. Android 的 slot host
HomeContainerSurfaceView 将 React children 分为两类:engine child 直接铺满 Surface,HomeContainerSlotView 先进入隐藏 parking view。每次布局:
- 通过 child tree 找到
HomeContainerView; - 把 slot key 集合传给 engine;
- 调用
slotHostView(key)找到 header/tab/page 内部的 host; - host 尚未 attach 或尺寸为 0 时继续 park;
- host 可见时将 slot reparent 到 host,tab accessory 以 36dp 居中,其余 slot 填满 host;
- owner 不匹配时把
ownerAuthorized设为 false,触摸直接吞掉、accessibility 隐藏。
7. Android 的小结
Android 方案的强项是复用平台成熟的 ViewPager2 + RecyclerView + SwipeRefreshLayout,把复杂度集中在 transition、nested gesture 和自定义 layout;它的主要风险是 RecyclerView 的异步 attach/layout 时机,因此 slot layout change 和 initial commit 都被显式建模。