
简介这是一份面向Android开发初学者与UI动效实践者的翻页动画源码学习资源聚焦书籍翻页效果的原生实现方案适用于电子书阅读器、数字杂志等需要沉浸式交互体验的应用开发场景。资源共155个文件包含54个Java核心逻辑类涵盖FlipView控制器、手势识别、动画插值器等、35个XML布局与属性配置文件支持多屏幕适配与主题定制、28个PNG与9个JPG翻页纹理及过渡图辅以properties国际化配置、txt说明文档及iml/project工程元数据整体压缩包仅1.8MB轻量易导入。已有132人下载学习可直接运行Aphid-FlipView-Demo.apk与Flip-Touch.apk双示例APK快速验证左右翻页、触控拖拽、阴影渐变等关键效果代码结构清晰含独立Library模块与Demo模块分离设计便于理解组件解耦思路与复用方法。1. 这不是动画效果演示而是 Android 系统级翻页交互的源码级实现你打开一个电子书阅读器手指向左一划页面像纸张一样卷起、翻转、消失——这个“左右翻页翻书”动效背后不是调用ViewPager2或RecyclerView的简单滑动监听而是直接基于 Android 框架层ViewGroup布局逻辑 Canvas手动绘制 Scroller物理惯性模拟 Matrix变形控制的完整翻页引擎。这份名为“安卓Android源码——左右翻页翻书.zip”的压缩包本质是一套可嵌入任意 Activity 的、零依赖的纯 Java 翻页组件源码非 AAR 库它绕过了ViewPager的预加载机制和FragmentStatePagerAdapter的生命周期开销专为高帧率、低内存、强可控的书籍类应用设计。适合正在用 Android Studio 开发阅读类 App 的中高级开发者如果你已卡在onDraw()中手动裁剪翻页区域、搞不定dispatchTouchEvent()与onInterceptTouchEvent()的协同拦截、或发现ValueAnimator驱动的翻页在低端机上掉帧严重——这份源码就是你该拆解的第一手参考。它不教你怎么写 Hello World只解决一个具体问题如何让一页 PDF 或 Bitmap 在 Android 上以物理可信的方式完成一次左右翻页。2. 翻页逻辑的核心三要素手势识别、页面状态机与 Canvas 绘制管线2.1 手势识别必须脱离 GestureDetector自建滑动方向与翻页阈值判定Android 系统自带的GestureDetector对翻页场景存在根本性缺陷它将onScroll()视为连续位移事件但真实翻页需要区分“拖拽中”、“松手后自动翻”、“松手后回弹”三种状态且需精确捕获手指离开屏幕瞬间的瞬时速度。本源码采用VelocityTracker 自定义TouchSlop计算方案Override public boolean onTouchEvent(MotionEvent event) { velocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX event.getX(); isDragging true; break; case MotionEvent.ACTION_MOVE: float dx event.getX() - lastX; if (Math.abs(dx) mTouchSlop !isFlipping) { isDragging true; // 启动翻页状态机进入拖拽模式 flipState FLIP_DRAGGING; lastX event.getX(); invalidate(); // 触发重绘 } break; case MotionEvent.ACTION_UP: velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); float velocityX velocityTracker.getXVelocity(); // 松手时速度 300dp/s 且位移 1/4 屏宽 → 自动翻页 if (Math.abs(velocityX) 300 Math.abs(getFlipProgress()) 0.25f) { startAutoFlip(velocityX 0 ? DIRECTION_RIGHT : DIRECTION_LEFT); } else { // 回弹到当前页或邻页 snapToPage(); } velocityTracker.clear(); break; } return true; }提示mTouchSlop必须设为ViewConfiguration.get(context).getScaledTouchSlop()的 1.5 倍否则在高 PPI 屏幕如 Pixel 7上易误触发mMaximumVelocity需设为ViewConfiguration.get(context).getScaledMaximumFlingVelocity()否则computeCurrentVelocity()返回值失真。2.2 页面状态机用枚举定义 5 种翻页阶段避免布尔标志位爆炸源码中FlipState枚举明确划分了翻页全生命周期状态触发条件onDraw()行为关键变量FLIP_IDLE初始态或翻页结束绘制完整当前页currentPage 0FLIP_DRAGGING手指拖拽中绘制当前页翻页页的叠加变形flipProgress 0.0f ~ 1.0fFLIP_AUTO_FLIPPING松手后自动翻页启动Scroller.startScroll()scroller.getCurrX()实时驱动FLIP_SNAPPING回弹动画中插值计算目标页偏移snapInterpolator new AccelerateDecelerateInterpolator()FLIP_COMPLETE动画结束重置状态触发onPageChanged()notifyPageChanged(newPage)这种状态机设计直接规避了传统方案中isFlipping,isSnapping,isAnimating等多个布尔变量交叉判断导致的逻辑漏洞。例如当FLIP_AUTO_FLIPPING状态下用户再次触摸源码会立即调用scroller.abortAnimation()并切换至FLIP_DRAGGING确保手势响应无延迟。2.3 Canvas 绘制管线用Matrix实现纸张卷曲变形而非简单 Alpha 渐变真正的翻页效果核心在于Canvas.drawBitmap()的Matrix变形控制。源码中drawFlipPage()方法分三步完成裁剪翻页区域用Path构建动态贝塞尔曲线路径模拟纸张卷边弧度计算翻页页变形矩阵根据flipProgress动态生成Matrix对翻页页 Bitmap 施加透视缩放 X 轴旋转分层绘制先绘制当前页未变形再用clipPath()截取翻页页可见区域最后绘制变形后的翻页页关键代码段private void drawFlipPage(Canvas canvas, Bitmap currentPage, Bitmap nextPage, float progress) { // 步骤1构建卷边路径简化版实际源码含 4 控制点贝塞尔 Path foldPath new Path(); float foldWidth getWidth() * progress; // 当前翻页宽度 foldPath.moveTo(getWidth() - foldWidth, 0); foldPath.lineTo(getWidth(), 0); foldPath.lineTo(getWidth(), getHeight()); foldPath.lineTo(getWidth() - foldWidth, getHeight()); foldPath.close(); // 步骤2为翻页页创建变形 Matrix Matrix matrix new Matrix(); float scale 1.0f - progress * 0.3f; // 翻页页随进度缩小 float rotation progress * 90; // 从 0° 旋转到 90° matrix.postScale(scale, scale, getWidth(), getHeight() / 2f); matrix.postRotate(rotation, getWidth(), getHeight() / 2f); // 步骤3分层绘制 canvas.save(); canvas.clipPath(foldPath); // 仅在卷边区域内绘制 canvas.drawBitmap(nextPage, matrix, paint); canvas.restore(); // 绘制当前页覆盖未翻区域 canvas.drawBitmap(currentPage, 0, 0, paint); }注意clipPath()在 Android 8.0 上性能极佳但低于 API 26 时需降级为canvas.save()canvas.clipRect()源码中通过Build.VERSION.SDK_INT Build.VERSION_CODES.O分支处理这是保证低端机如 Android 5.1 的红米 Note 3不卡顿的关键。3. 在 Android Studio 中集成与调试从解压到真机验证的完整链路3.1 源码结构解析与模块化接入策略解压左右翻页翻书.zip后目录结构如下src/ ├── com/example/flipbook/ │ ├── FlipBookView.java // 核心 View 类继承 ViewGroup │ ├── FlipPageRenderer.java // Canvas 绘制逻辑封装 │ ├── FlipState.java // 状态机枚举 │ └── FlipCallback.java // 页面切换回调接口 res/ ├── values/attrs_flipbook.xml // 自定义属性flipDirection, flipDuration 等集成时严禁直接复制FlipBookView.java到项目中并修改。正确做法是将com.example.flipbook包名改为你的应用包名如com.yourapp.ui.flip在attrs_flipbook.xml中添加declare-styleable声明flipModeleft|right|auto等属性在FlipBookView构造函数中通过TypedArray读取自定义属性而非硬编码这样做的好处是后续可通过app:flipModeright在 XML 中配置符合 Android Studio 的可视化编辑习惯且便于多页面复用。3.2 在 Activity 中初始化并绑定数据源假设你已有ListBitmap存储书籍页面初始化代码如下public class BookActivity extends AppCompatActivity { private FlipBookView flipBookView; private ListBitmap pageBitmaps; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book); flipBookView findViewById(R.id.flip_book_view); pageBitmaps loadPageBitmaps(); // 你的加载逻辑建议用 Glide 加载本地 assets // 关键设置页面提供者解耦数据与 UI flipBookView.setPageProvider(new FlipBookView.PageProvider() { Override public int getPageCount() { return pageBitmaps.size(); } Override public Bitmap getPageBitmap(int pageIndex) { if (pageIndex 0 || pageIndex pageBitmaps.size()) { return null; } return pageBitmaps.get(pageIndex); } }); // 设置翻页回调 flipBookView.setFlipCallback(new FlipCallback() { Override public void onPageChanged(int newPageIndex, int oldPageIndex) { // 更新底部页码指示器 updatePageIndicator(newPageIndex); // 预加载下一页可选 preloadNextPage(newPageIndex); } }); } }提示setPageProvider()是源码设计的亮点——它强制将页面数据获取逻辑与 UI 分离。这意味着你可以轻松替换为PDFRenderer用PdfRenderer解析 PDF、WebView渲染 HTML 页面甚至SurfaceView播放视频帧而无需修改FlipBookView一行代码。3.3 真机调试必查的 3 个性能瓶颈点在 Android Studio 的 Profiler 中重点监控以下三项监控项正常值异常表现排查命令Jank掉帧 5%翻页过程中频繁出现 30fps 以下adb shell dumpsys gfxinfo com.yourappMemory Alloc内存分配单次翻页 512KB每次onDraw()触发大量Bitmap创建在FlipPageRenderer.java中搜索new Bitmap确认是否复用BitmapFactory.Options.inMutable trueCPU UsageCPU 占用翻页时 40%Scroller.computeScrollOffset()占用过高用systrace录制python systrace.py -t 10 -a com.yourapp gfx view wm am特别注意源码中FlipBookView的onDraw()方法内禁止调用Bitmap.createBitmap()。所有页面 Bitmap 必须由PageProvider预加载并缓存否则在onDraw()中实时 decode 会导致严重 GC 卡顿。若发现createBitmap()调用应立即改用BitmapFactory.decodeResource(getResources(), R.drawable.page_1, options)并设置inMutable false。4. 翻页参数调优与边界场景处理让效果适配不同设备与内容类型4.1 3 个必调参数及其物理意义源码通过attrs_flipbook.xml暴露三个核心参数它们直接决定翻页的“手感”属性默认值物理意义调优建议flipDuration300ms翻页动画总时长小屏手机5.5设为 250ms大屏平板10设为 350ms模拟真实纸张惯性flipEasingaccelerate_decelerate插值器类型避免linear机械感强推荐cubicPathInterpolator构建 S 曲线flipThreshold0.25f松手后自动翻页的最小进度阈值在 Android 12 上可降至 0.2f因MotionLayout优化了触摸预测在 XML 中配置示例com.yourapp.ui.flip.FlipBookView android:idid/flip_book_view android:layout_widthmatch_parent android:layout_heightmatch_parent app:flipDuration280 app:flipEasingcubic app:flipThreshold0.22 /提示cubic插值器需在代码中动态创建Path path new Path(); path.moveTo(0, 0); path.cubicTo(0.2f, 0, 0.8f, 1f, 1f, 1f); // S 型曲线 scroller new Scroller(getContext(), new PathInterpolator(path));4.2 处理 4 类典型边界场景场景1第一页向左翻 / 最后一页向右翻 —— 阻止越界源码中canFlipLeft()和canFlipRight()方法严格校验private boolean canFlipLeft() { return currentPage 0 flipState ! FLIP_COMPLETE; } private boolean canFlipRight() { return currentPage getPageCount() - 1 flipState ! FLIP_COMPLETE; }在ACTION_MOVE中加入判断if (dx 0 !canFlipRight()) { // 向右拖拽但已是最后一页 → 抑制翻页仅做视觉反馈如轻微缩放 handleEdgeBounce(DIRECTION_RIGHT); return true; }场景2快速连续翻页导致状态错乱解决方案是引入flipLock标志位 Handler延迟重置private void startAutoFlip(int direction) { if (flipLock) return; // 锁定中拒绝新翻页 flipLock true; // ... 启动 Scroller handler.postDelayed(() - flipLock false, 350); // 翻页完成后解锁 }场景3横竖屏切换时页面错位重写onConfigurationChanged()Override public void onConfigurationChanged(NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); // 重新计算页面尺寸重置翻页进度 resetFlipProgress(); requestLayout(); }场景4后台切回前台时动画残留在onResume()中强制重置Override protected void onResume() { super.onResume(); if (flipBookView ! null) { flipBookView.resetToIdleState(); // 源码提供的重置方法 } }5. 进阶技巧将翻页效果与系统级特性深度集成5.1 适配 Android 12 的 Material You 动态色彩Material You 要求 UI 元素响应系统主题色变化。翻页页的“纸张阴影”和“卷边高光”需动态提取ColorScheme// 在 FlipBookView 中监听主题变更 private void setupDynamicColors() { if (Build.VERSION.SDK_INT Build.VERSION_CODES.S) { final ColorScheme colorScheme getColorScheme(); // 提取主色用于卷边高光 int highlightColor colorScheme.primary; // 提取背景色用于纸张底色 int paperColor colorScheme.background; updatePaperColors(highlightColor, paperColor); } } private void updatePaperColors(int highlight, int paper) { // 修改 FlipPageRenderer 中的 paint.setColor() highlightPaint.setColor(highlight); paperPaint.setColor(paper); }5.2 与 Jetpack Compose 混合开发用AndroidView封装若项目已迁移到 Compose可通过AndroidView复用该翻页组件Composable fun FlipBookComposable( pageBitmaps: ListBitmap, onFlip: (Int) - Unit ) { AndroidView( factory { context - FlipBookView(context).apply { setPageProvider(object : FlipBookView.PageProvider { override fun getPageCount(): Int pageBitmaps.size override fun getPageBitmap(pageIndex: Int): Bitmap? pageBitmaps.getOrNull(pageIndex) }) setFlipCallback(object : FlipCallback { override fun onPageChanged(newPageIndex: Int, oldPageIndex: Int) { onFlip(newPageIndex) } }) } }, modifier Modifier.fillMaxSize() ) }5.3 性能极致优化用 RenderScript 替代 CPU 矩阵运算可选对于 4K 高清书籍页Matrix变形在 CPU 上计算耗时可能超 8ms。此时可启用 RenderScript 加速// 在 FlipPageRenderer.java 中 private void initRenderScript(Context context) { rs RenderScript.create(context); scriptC_flip new ScriptC_flip(rs); // 将 Bitmap 上传至 Allocation inputAllocation Allocation.createFromBitmap(rs, bitmap); outputAllocation Allocation.createTyped(rs, inputAllocation.type); }注意RenderScript 在 Android 12 已标记为 deprecated但对android.renderscript的兼容性支持将持续至 Android 14。若目标 SDK ≥ 33应改用Vulkan或OpenGL ES 3.0的GLSLshader 实现相同卷边效果源码中已预留ShaderFlipRenderer接口。翻页效果的最终评判标准不是“看起来像”而是“操作起来信”。当你在 Android Studio 中拖动FlipBookView的flipProgress滑块看到页面如真实纸张般产生透视收缩、边缘卷曲、阴影渐变并且在红米 Note 8Android 10和 Pixel 7Android 13上均保持 60fps 稳定帧率——你就已经掌握了这份源码最核心的价值它把 Android 图形系统的底层能力拧成了一根可复用、可调试、可演进的翻页螺丝钉。本文还有配套的精品资源点击获取