FoldableLayout 详解:Android 纸张折叠动画
你能想象在 Android 上实现"一张纸对折展开"的真实物理动画吗?不是简单的缩放或淡入淡出,而是像真实纸张一样沿折痕折叠、有光影变化、有厚度感的 3D 动画。Alex Vasilkov 的 FoldableLayout 就是实现这种效果的开源库——1.6k+ Star,Android 上最经典的"纸张折叠"动画组件。虽然库已停止维护(最后更新 2018),但它展示的Canvas 2D 仿 3D 折叠技巧至今仍是自定义动画 View 的教科书。本文讲清它是什么、核心原理怎么实现,以及为什么即使归档仍值得学习。
GitHub 仓库:alexvasilkov/FoldableLayout(1.6k+ Star);实现原理博客:Creating Paper Folding Animation in Android。
1、FoldableLayout 是什么
FoldableLayout 是一组 Android 自定义 ViewGroup,核心效果是"沿水平或垂直方向把内容像纸一样折叠/展开"。视觉效果非常逼真:内容沿折痕对折,折叠过程中折痕处有阴影渐变(模拟纸张弯曲的背光),折叠后的"背面"也有独立的内容。整个动画流畅自然,不像"假的 2D 模拟",而像真实的物理折叠。
// 核心定位
FoldableLayout = 沿折痕折叠/展开内容的 Android 自定义 ViewGroup
// 两个主要组件
// FoldableLayout:单折页(内容沿一条折痕对折)
// FoldableItemLayout:可折叠的 item(用于列表/网格中每个 item 独立折叠)
// 基本面
// 作者:Alex Vasilkov(同作者还有 GestureViews、FlabbyView 等知名 UI 库)
// 语言:Java
// 星标:1,648 Star
// 最后更新:2018-02(已停维,但代码稳定可用)
// 许可:Apache 2.0
// 体积:310 个方法,31 KB(极轻量)
// minSdk:14(Android 4.0+)2、视觉效果:两种经典用法
// 用法一:Unfoldable Details(展开详情)
// 场景:列表点击后,详情页"从折叠状态展开"出现
// 效果:像翻开一张折叠的纸,内容从折痕处逐渐展开铺满屏幕
// 适用:商品详情、文章展开、卡片放大
// 用法二:Foldable List(折叠列表)
// 场景:列表/网格中每个 item 都可以折叠/展开
// 效果:item 像折纸一样对折收缩或展开恢复
// 适用:可收放的设置项、手风琴菜单、创意列表交互
// 两种用法的共同点:都基于 FoldableLayout 的"折叠引擎"
// 区别只在"用在哪"——详情页用 FoldableLayout,列表用 FoldableItemLayout3、核心原理:用 2D Canvas 模拟 3D 折叠
FoldableLayout 最值得学的不是"一个现成效果",而是它在 Android 的 2D Canvas 上模拟出 3D 折叠的技巧——没有用 OpenGL、没有用 RenderScript、没有用 3D 引擎,纯靠 Canvas + Matrix + 阴影绘制就骗过了眼睛。核心思路分三层:
// 第一层:把内容切成"折叠段"
// 将整个内容沿折叠方向分成若干个"折叠段"(fold)
// 每段是一个独立的绘制区域,可以独立变换
// 第二层:用 Matrix 做透视压缩(模拟折叠角度)
// 折叠角度从 0 度(完全展开)到 180 度(完全对折)
// 用 Matrix.setScale 做 X 方向压缩(模拟"折过去的厚度")
// 折叠 90 度时,段宽度压缩为 0("侧面对着你")
// 折叠 180 度时,段完全翻转("背面对着你")
// 第三层:用渐变阴影模拟"折痕光影"
// 折痕处画一道从暗到亮的 LinearGradient
// 折叠角度越大,阴影越深(模拟纸张弯曲时的背光)
// → 这层阴影是"看起来像 3D"的关键
// 没有它,折叠只是"2D 压缩";有了它,才像"纸弯曲了"这三层叠加,就产生了一个令人信服的"纸张折叠"错觉。其中阴影渐变是最巧妙的——它不改变内容的形状,只在折痕处叠一层"暗→亮"的渐变,视觉上就产生了"纸面弯曲"的 3D 感。这个技巧在所有"2D 仿 3D"的场景中都适用(翻页效果、书本翻页、卡片翻转)。
4、折叠动画的数学
// 折叠进度:foldRotation 从 0 到 180 度
// 0 度 = 完全展开(平面)
// 90 度 = 半折(侧面对观察者,宽度为 0)
// 180 度 = 完全对折(背面朝向你)
// 每个折叠段的宽度变化(透视投影):
// 实际宽度 = 原始宽度 × |cos(foldRotation)|
// → 0 度时 = 原始宽度
// → 90 度时 = 0(侧面消失)
// → 180 度时 = 原始宽度(背面展开)
// 阴影强度随折叠角度变化:
// 阴影 alpha = sin(foldRotation) × maxShadowAlpha
// → 0 度时 = 0(无阴影,完全展开)
// → 90 度时 = 最大阴影(折痕最深)
// → 180 度时 = 0(背面完全展开,无折痕)
// 整体动画由 ValueAnimator 驱动 foldRotation 从 0 到 180(或反向)
// 每帧根据 foldRotation 重算所有折叠段的 Matrix 和阴影 → invalidate 重绘这个数学模型很优雅——cos 控制宽度(折叠段的"可视宽度"),sin 控制阴影(折痕的"深度感"),两个三角函数一组合,就模拟出了完整的折叠物理效果。理解了这个模型,你不仅能做折叠,还能做翻页、翻转、书本效果——它们都是"旋转角度驱动可视宽度和阴影"的变体。
5、集成与使用
// Gradle 依赖
dependencies {
implementation 'com.alexvasilkov:foldable-layout:1.2.1'
}
// XML 布局:FoldableLayout(单折页)
// <com.alexvasilkov.foldablelayout.FoldableLayout
// android:id="@+id/foldable_layout"
// android:layout_width="match_parent"
// android:layout_height="match_parent">
//
// <!-- 第一个子 View:折叠后的"正面"内容 -->
// <LinearLayout ... />
//
// <!-- 第二个子 View(可选):折叠后的"背面"内容 -->
// <LinearLayout ... />
//
// </com.alexvasilkov.foldablelayout.FoldableLayout>
// 代码控制折叠/展开
val foldableLayout = findViewById<FoldableLayout>(R.id.foldable_layout)
foldableLayout.foldToAngle(0f) // 完全展开
foldableLayout.foldToAngle(180f) // 完全对折
// 设置折叠方向
foldableLayout.setOrientation(FoldableLayout.VERTICAL) // 或 HORIZONTAL- 注意:库已停维(2018),compile 需改为 implementation,support 库需手动迁移 AndroidX
- 推荐拷源码集成(代码量不大),避免依赖冲突
6、FoldableItemLayout:列表中的折叠
// FoldableItemLayout 用于在列表/网格中给每个 item 独立的折叠效果
// 典型用法:RecyclerView 的 ViewHolder 里包裹 FoldableItemLayout
class MyViewHolder(val foldableItem: FoldableItemLayout) : RecyclerView.ViewHolder(foldableItem) {
fun bind(item: Item) {
// 每个 item 可以独立设置折叠角度
foldableItem.setFoldRotation(0f) // 展开
// 或动画折叠
foldableItem.animate().foldRotation(180f).start()
}
}
// 效果:列表中每个 item 像一张可折叠的卡片,点击后对折或展开
// 配合 staggered 动画(每个 item 延迟不同时间折叠),效果非常炫酷7、与同类效果对比
效果 库 原理 星标
----------------------------------------------------------------------------
纸张折叠 FoldableLayout Canvas 分段 + Matrix 1.6k
翻页(书本) PageFlip / AndroidPageCurl 贝塞尔曲线 + 阴影 各 ~1k
卡片 3D 翻转 CardFlipAnimation rotationY + Camera ~500
折叠面板 ExpandableLayout 高度展开/收起(非真折叠) ~2kFoldableLayout 的独特之处在于"真折叠"——不是简单的高度展开收起(那是 ExpandableLayout),而是沿折痕物理对折,有厚度、有阴影、有正反面。在同类"翻折效果"开源库中,它的折叠质感最真实。
8、适用场景与局限
- 适合:详情页展开动画(列表点击→详情"展开")、创意交互(折纸风格 UI)、引导页(折叠揭示内容)
- 适合:需要"质感"的列表交互(卡片折叠/展开)、电子书翻页类场景
- 局限1:库已停维(2018),需手动迁移 AndroidX + 改 compile→implementation
- 局限2:不兼容 Compose(纯 View 体系)
- 局限3:复杂内容(如 WebView / SurfaceView)在折叠变换时可能渲染异常
- 局限4:性能:每帧都要切分 + 绘制多个折叠段,低配设备上列表滚动时注意帧率
9、总结
FoldableLayout 是 Alex Vasilkov 出品的 Android 纸张折叠动画库,核心原理是用 2D Canvas + Matrix 分段 + 渐变阴影模拟 3D 折叠——把内容沿折痕切成多段、每段按 cos 控制宽度、用 sin 控制阴影深度,纯 2D 就骗出了"真纸折叠"的效果。虽然库已停维,但它的"2D 仿 3D"技巧至今仍是自定义动画 View 的教科书级参考。
关键要点:
- 定位:沿折痕折叠/展开内容的自定义 ViewGroup,1.6k Star
- 两大组件:FoldableLayout(详情页展开)+ FoldableItemLayout(列表 item 折叠)
- 核心技巧:内容分段 + Matrix(cos) 控制宽度 + LinearGradient(sin) 模拟折痕阴影
- cos 控制宽度、sin 控制阴影——两个三角函数一组合,2D 仿出 3D
- 停维但代码稳定:拷源码集成 + 手动迁移 AndroidX 即可复用
对于想在 Android 上做"物理感十足"动画的开发者而言,FoldableLayout 是一个"小而精"的学习对象——它用 31 KB 的代码,演示了"如何用纯 Canvas 2D 逼真模拟 3D 物理效果"。库会停维,但"cos 控制宽度 + sin 控制阴影"这个技巧永远不会过时。读懂它,你掌握的不是"一个折叠效果",而是"用 2D 欺骗 3D 感知"的通用能力——翻页、翻转、弯曲、透视,原理一脉相承。
参考资料:
