Skip to content

第 08 章:Android —— ViewPager2、RecyclerView 与手势转发 ​

1. Android 的页面骨架 ​

HomeContainerView 继承 SwipeRefreshLayout,内部是一个 FrameLayout,再放入三类 child:

text
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。代码因此维护:

  • pendingPagerTabId
  • pendingPagerSelectionIsProgrammatic
  • pendingPagerSelectionShouldNotify
  • pagerScrollState

只有 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 会:

  1. 判断触点是否在 header/tabs;
  2. 计算 dx/dy 与 touch slop;
  3. 区分横向 tab 手势和纵向折叠手势;
  4. 对纵向手势,将 MotionEvent 坐标转换后转发给当前 page 的 RecyclerView;
  5. 让 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。每次布局:

  1. 通过 child tree 找到 HomeContainerView;
  2. 把 slot key 集合传给 engine;
  3. 调用 slotHostView(key) 找到 header/tab/page 内部的 host;
  4. host 尚未 attach 或尺寸为 0 时继续 park;
  5. host 可见时将 slot reparent 到 host,tab accessory 以 36dp 居中,其余 slot 填满 host;
  6. owner 不匹配时把 ownerAuthorized 设为 false,触摸直接吞掉、accessibility 隐藏。

7. Android 的小结 ​

Android 方案的强项是复用平台成熟的 ViewPager2 + RecyclerView + SwipeRefreshLayout,把复杂度集中在 transition、nested gesture 和自定义 layout;它的主要风险是 RecyclerView 的异步 attach/layout 时机,因此 slot layout change 和 initial commit 都被显式建模。

基于 app-monorepo 2026-07-31 源码快照整理 · 非官方文档