material-scrolling源码精读:RecyclerViewHolder如何桥接ObservableRecyclerView滚动状态

发布时间:2026/8/27 16:23:05
material-scrolling源码精读:RecyclerViewHolder如何桥接ObservableRecyclerView滚动状态 material-scrolling源码精读RecyclerViewHolder如何桥接ObservableRecyclerView滚动状态【免费下载链接】material-scrollingAndroid library for material scrolling techniques.项目地址: https://gitcode.com/gh_mirrors/ma/material-scrollingmaterial-scrolling 是一款 Android Material 滚动技术方案库把 Google 设计规范的滚动交互变成几行 API。本文带你逐段精读它的核心源码RecyclerViewHolder究竟如何桥接ObservableRecyclerView的滚动状态把一次触摸手势变成标题平移、背景视差、FAB 缩放的完整链路。一句话总览ObservableRecyclerView 感知手势 → RecyclerViewHolder 归一化坐标 → BehaviorDispatcher 广播 → Behavior 们各自动画。模块导航先看地图再读代码 ️模块文件角色library/src/main/java/jp/satorufujiwara/scrolling/RecyclerViewHolder.java本文主角滚动状态桥接器library/src/main/java/jp/satorufujiwara/scrolling/MaterialScrollingLayout.java容器布局负责接线library/src/main/java/jp/satorufujiwara/scrolling/MaterialScrollingViewPager.javaViewPager 多页联动版本library/src/main/java/jp/satorufujiwara/scrolling/BehaviorDispatcher.java事件广播中心library/src/main/java/jp/satorufujiwara/scrolling/Behavior.java所有滚动行为的抽象基类library/src/main/java/jp/satorufujiwara/scrolling/Utils.java递归查找 ObservableRecyclerView 的工具sample/src/main/java/jp/satorufujiwara/scrolling/sample/MainActivity.java示例入口看真实用法想看完整演示README 在arts/目录下附了三段 GIFviewpager.gif、imagefab.gif、viewpager_land.gif分别展示横向翻页、大图 FAB、横屏效果。核心机制滚动事件如何过桥RecyclerView 本身只回调我滚动了不关心页面上其他 View 怎么动。material-scrolling 的答案是中间层桥接触摸手势 │ ▼ ObservableRecyclerView ── 注册回调 ──▶ RecyclerViewHolder (桥) │ 归一化坐标 scrollY 增量 dy │ 支持开关 / 缓存 / 同步滚动 ▼ BehaviorDispatcher (广播) │ ┌────────────────────────┼────────────────────┐ ▼ ▼ ▼ ScrollBehavior ParallaxBehavior FabBehavior (标题跟手平移) (背景视差偏移) (FAB 缩放消失)逐段精读RecyclerViewHolder 的 7 个关键设计以下路径均相对仓库根目录核心文件为 library/src/main/java/jp/satorufujiwara/scrolling/RecyclerViewHolder.java。1️⃣ 构造即接线两行代码完成桥接this.recyclerView recyclerView; this.recyclerView.setClipToPadding(false); recyclerView.setScrollViewCallbacks(new OnScrollDelegate(this));持有ObservableRecyclerView引用并把回调插线到内部类OnScrollDelegate第 72–94 行——它只干一件事把onScrollChanged、onDownMotionEvent、onUpOrCancelMotionEvent三个事件原样转发给 holder 自己。setClipToPadding(false)允许列表内容画进 padding 区顶部大图的视觉空间由此而来。2️⃣ flexibleHeight让 RecyclerView 自带头部留白setFlexibleHeight()第 28–31 行把布局属性ms_flexible_height转成RecyclerView 顶部 paddingapp:ms_flexible_height240dp于是标题区不再是一个独立 View而是列表自带的留白区域——这是整个方案的坐标系基准。3️⃣ 坐标归一化把列表内部位置换算成页面滚动量getScrollY()第 37–39 行是桥的核心return isDirty ? scrollY : recyclerView.getCurrentScrollY() flexibleHeight;实时位置 getCurrentScrollY() flexibleHeight这样 scrollY 在大图完全收起时恰好等于 flexibleHeight后续所有 Behavior 都用同一把尺子isDirty标志当scrollTo()主动滚动后回调还没来得及回来就优先返回缓存值保证 ViewPager 同步多页时坐标不穿帮见第 4 节。4️⃣ 增量事件一次滚动只发差值void onScrolled(final int y) { isDirty false; if (!isDispatchScroll) return; scrollY y flexibleHeight; behaviorDispatcher.onScrolled(scrollY, scrollY - preScrollY); preScrollY scrollY; }发给 Behavior 的是(scrollY, dy)两个参数——绝对位置 本次增量。dy让行为可以写跟手回弹惯性感知这类依赖速度的效果。5️⃣ 主动滚动scrollTo 的反向桥isDirty true; scrollY y; final LinearLayoutManager lm (LinearLayoutManager) recyclerView.getLayoutManager(); lm.scrollToPositionWithOffset(0, -y);桥是双向的MaterialScrollingViewPager翻页时会用holder.scrollTo(...)把非当前页同步到当前页位置见 MaterialScrollingViewPager.java 的onPageScrollStateChanged这就是多页齐刷刷一起收起的效果来源。6️⃣ 事件开关setIsDispatchScrollMaterialScrollingLayout在addView时创建 holder、removeView时关闭开关MaterialScrollingLayout.java 第 50–67 行。ViewPager 场景更讲究onPageSelected时先关闭所有 holder 的开关只打开当前页那个——彻底杜绝旧页残留回调污染动画。7️⃣ 手势生命周期按下 / 抬起同样被桥接onDownMotionEvent与onUpOrCancelMotionEvent(ScrollState)也全部透传给BehaviorDispatcher。Behavior 拿到ScrollStateFLING / DRAG / IDLE就能做抬手后回弹的决策桥接的不仅是位置还有手势语义。三个最容易踩坑的细节 ⚠️isDirty不是脏检查它标记的是我刚执行过主动scrollTo回调值不可信读源码时别按字面理解。开关先于缓存setIsDispatchScroll(false)只拦事件getScrollY()缓存值仍然有效两者职责分离。坐标系偏移所有 Behavior 收到的 scrollY 都已 flexibleHeight写自定义行为时直接用getFlexibleHeight()做上限即可例如内置的 ScrollBehavior.javaViewCompat.setTranslationY(target, -Math.min(scrollY, getFlexibleHeight()));上手只需两步 第一步布局里包一层容器并声明 flexible 区域见 library/src/main/res/values/attrs.xml 定义的属性jp.satorufujiwara.scrolling.MaterialScrollingLayout app:ms_flexible_height240dp com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView android:layout_widthmatch_parent android:layout_heightmatch_parent/ /jp.satorufujiwara.scrolling.MaterialScrollingLayout第二步代码里注册行为一行一个效果materialScrollingLayout.addBehavior(bgImageView, new ParallaxBehavior()); materialScrollingLayout.addBehavior(titleTextView, new ScrollingBehavior());内置行为都在 library/src/main/java/jp/satorufujiwara/scrolling/behavior/ 下自定义只需继承 Behavior.java 重写onScrolled范例参考 sample 的 FabBehavior 与 TitleBehavior。依赖jcenterjp.satorufujiwara:material-scrolling:1.2.1com.github.ksoichiro:android-observablescrollview:1.6.0。总结这张桥值得抄的设计归一化把列表内部滚动统一换算成页面滚动量一套坐标打天下可插拔回调、开关、缓存三个正交机制让单页布局和多页 ViewPager 共用同一个 holder广播式一个 Dispatcher 一对多分发Behavior 之间零耦合。想动手验证先 clone 仓库https://gitcode.com/gh_mirrors/ma/material-scrolling跑 sample 工程对照本文的 7 个设计点逐行读你会发现不到 100 行的RecyclerViewHolder才是整套 Material 滚动魔法的引擎。【免费下载链接】material-scrollingAndroid library for material scrolling techniques.项目地址: https://gitcode.com/gh_mirrors/ma/material-scrolling创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考