# PTQFlipper
**Repository Path**: 110java/PTQFlipper
## Basic Information
- **Project Name**: PTQFlipper
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-06-27
- **Last Updated**: 2026-06-27
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# PTQBookPageView
Android JetPack Compose 仿真书籍翻页组件,支持自定义任何非动态内容。
[效果演示] [DemoApk v1.1.0](https://pan.baidu.com/s/1bgmC1-suqdjMNkLQ2YOx-Q) 提取码:ptq6
[项目仓库] [Github](https://github.com/FantasticPornTaiQiang/PTQFlipper)
[组件使用] [传送门](https://juejin.cn/post/7236281103221375033)
[设计思路] [传送门](https://juejin.cn/post/7236636296876818491)
[源码解析]
* [算法部分](https://www.bilibili.com/video/BV1s24y1A7xM)
* [绘制和动画部分](https://www.bilibili.com/video/BV1FP411X7Vu)(包括文字扭曲、点击动画、阴影等效果)
(DemoApk 中的图片均使用 AI 生成的图片)
---
## 1 使用组件
### 1.1 引入
1、在项目的 root 的 build.gradle 中引入仓库。(高版本gradle是在settings.gradle里配)
```groovy
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
```
2、在项目的 app 的 build.gradle 中添加依赖。
```groovy
dependencies {
implementation 'com.github.FantasticPornTaiQiang:PTQFlipper:$latest_version'
}
```
[最新版本](https://jitpack.io/#FantasticPornTaiQiang/PTQFlipper)
### 1.2 基本使用
在@Composable 中使用组件。
```kotlin
val state by rememberPTQBookPageViewState(pageCount = 100)
PTQBookPageView(state = state) {
contents { currentPage, refresh ->
Box(modifier = Modifier.fillMaxSize().background(Color.White)) {
//任何非动态的自定义内容
}
refresh()
}
}
```
- 在 rememberPTQBookPageViewState 中**设置总页数**
- **请务必在 contents 的最末尾调用 refresh 方法以保证 View 显示的内容正常**
- 例如需要网络请求图片,那么请在请求成功后手动refresh,把新内容同步到组件内
- 在 contents 中利用回调参数 currentPage 设置**当前页显示的内容**
- 在 contents 中,最外层是一个 fillMaxSize 的 Box,并需要使用 background 设置**当前纸页正面**的颜色
- 在 contents 的 Box 中(即示例代码的注释处),**可以自定义任何非动态的内容**
**注:不支持:**
- 不支持纸张(即背景色)带透明度
- 不支持纸张设置圆角(即必须是矩形的)
- 暂时不支持像LazyColumn这种内部状态会快速变化的View(除非你能监听到状态停止变化的时刻,然后手动调用refresh,总之就是要保证refresh能调用到)
---
## 2 定制
### 2.1 页数控制
组件提供了[rememberPTQBookPageViewState](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/ptqbookpageview/src/main/java/ptq/mpga/ptqbookpageview/widget/PTQBookPageViewState.kt)方法进行页数相关的控制。
`rememberPTQBookPageViewState(pageCount: Int = 1, currentPage: Int? = null)`
- pageCount 总页数,如果页面总数小于当前页数,则会引发异常
- currentPage 当前页数,如果为 null 则页数由翻页器内部控制
控制页数方式有两种:
- 不传入 currentPage,则页数作为组件内部状态,由翻页器内部自动控制
- 传入 currentPage,则页数作为外部状态,需要在[onTurnPageRequest](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/ptqbookpageview/src/main/java/ptq/mpga/ptqbookpageview/widget/PTQBookPageViewScope.kt)方法中维护([详细介绍](#24-翻页请求))。
示例如下
```kotlin
var state by rememberPTQBookPageViewState(pageCount = 100, currentPage = 0)
PTQBookPageView(state = state) {
onTurnPageRequest { currentPage, isNextOrPrevious, success ->
if (!success) {
Toast.makeText(ctx, if (isNextOrPrevious) "已经是最后一页啦" else "已经是第一页啦", Toast.LENGTH_SHORT).show()
} else {
state = state.copy(currentPage = if (isNextOrPrevious) currentPage + 1 else currentPage - 1)
}
}
contents { currentPage, refresh ->
Box(modifier = Modifier.fillMaxSize().background(Color.White)) {
//任何非动态的自定义内容
Text(currentPage.toString(), modifier = Modifier.align(Alignment.Center))
}
refresh()
}
}
```
### 2.2 配置参数
组件提供了[rememberPTQBookPageViewConfig](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/ptqbookpageview/src/main/java/ptq/mpga/ptqbookpageview/widget/PTQBookPageViewState.kt)方法进行相关的控制。
`rememberPTQBookPageViewConfig(pageColor: Color = Color.White, disabled: Boolean = false)`
- pageColor **当前页背面**的颜色(页面正面的颜色在 contents 的 Box 的 background modifier 中设置)(暂不支持页面透明度的设置,即alpha通道必须为ff)
- disabled 整个组件是否禁用(若想单独禁用单击或拖动手势,请看[高级定制](#3-高级定制))
v1.1.0新增属性:
- distortionInterval 图像扭曲的采样间隔,可以根据屏幕大小去作适配,此值越小,曲线越精密,但同样地,计算开销会越大,性能会下降
- bezierEdgeDownSampling 边缘扭曲的采样间隔,可以根据屏幕大小去作适配,此值越大,曲线越精密,但同样地,计算开销会越大,性能会下降(变化不是线性的)
config 以参数的形式传入组件以生效,示例如下
```kotlin
val config by rememberPTQBookPageViewConfig()
PTQBookPageView(state = state, config = config) {
...
}
```
### 2.3 大小和位置
组件支持以 modifier 的形式设置大小和位置,具体地
- 支持以 padding modifier 方式设置组件位置和大小
- 支持以 height modifier 和 width modifier 方式设置大小
- **不支持**offset 和 size modifier
示例如下
```kotlin
PTQBookPageView(
modifier = Modifier.padding(start = padding[0].dp, top = padding[1].dp, end = padding[2].dp, bottom = padding[3].dp),
config = config,) {
...
}
```
```kotlin
PTQBookPageView(
modifier = Modifier.width(100.dp).height(100.dp),
config = config,) {
...
}
```
### 2.4 翻页请求
在[PTQBookPageViewScope](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/ptqbookpageview/src/main/java/ptq/mpga/ptqbookpageview/widget/PTQBookPageViewScope.kt)中注册`onTurnPageRequest`回调以响应手指的翻页请求。
`onTurnPageRequest(block: (currentPage: Int, isNextOrPrevious: Boolean, success: Boolean) -> Unit)`
当用户有想翻页的操作时,会触发这个回调。如果当前处于最后一页,仍想向右翻,则会翻页失败,但此回调仍然会调用,可以利用这个回调弹 Toast 显示没有下一页了。
- currentPage 用户操作之后的页面索引,范围是 [0, pageCount)
- isNextOrPrevious 用户想向前翻页还是向后翻页,true 表示 next
- success 用户翻页是否成功,若处于最后一页还想向右翻则翻页失败,处于第一页向前翻同理
[示例代码](#21-页数控制)
---
## 3 高级定制
### 3.1 控制点击时的翻页行为
在[PTQBookPageViewScope](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/ptqbookpageview/src/main/java/ptq/mpga/ptqbookpageview/widget/PTQBookPageViewScope.kt)中注册`tapBehavior`回调以自定义控制点击时的行为。
`tapBehavior(block: (leftUp: Point, rightDown: Point, touchPoint: Point) -> Boolean?)`
- leftUP 组件左上角坐标
- rightDown 组件右下角坐标
- touchPoint 用户手指触摸坐标
- 返回值 翻页行为,true=下一页,false=上一页,null=不响应
此回调中的 Point 均为相对于组件左上角的坐标点,且此回调会影响 UI 的呈现和[onTurnPageRequest](#24-翻页请求)回调。
如果不注册此回调,则默认情况下:
- 点击 x>1/2 处时翻下一页
- 点击 x<1/2 处时翻上一页
[示例代码](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/app/src/main/java/ptq/mpga/pinance/NovelActivity.kt)
### 3.2 控制拖动时的起手响应
在[PTQBookPageViewScope](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/ptqbookpageview/src/main/java/ptq/mpga/ptqbookpageview/widget/PTQBookPageViewScope.kt)中注册`responseDragWhen`回调以自定义控制拖动时的起手响应。
`responseDragWhen(block: (rightDown: Point, startTouchPoint: Point, currentTouchPoint: Point) -> Boolean?)`
- rightDown 组件右下角坐标(左上角为 0)
- startTouchPoint 用户起手坐标(开始动画前)
- currentTouchPoint 当前触摸点坐标
- 返回值 翻页行为,true=下一页(从右侧翻开),false=上一页(从左侧翻开),null=不响应
此回调决定了从起手开始到能够自由拖动之间的翻页行为,如果不注册此回调,默认情况下:
- 从右往左滑则右起手(翻下一页)
- 从左往右划则左起手(翻上一页)
[示例代码](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/app/src/main/java/ptq/mpga/pinance/NovelActivity.kt)
### 3.3 控制拖动松手时的翻页行为
在[PTQBookPageViewScope](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/ptqbookpageview/src/main/java/ptq/mpga/ptqbookpageview/widget/PTQBookPageViewScope.kt)中注册`dragBehavior`回调以自定义控制拖动松手时的翻页行为。
```
dragBehavior(block: (
rightDown: Point,
initialTouchPoint: Point,
lastTouchPoint: Point,
isRightToLeftWhenStart: Boolean
) -> Pair
)
```
- rightDown 组件右下角坐标(左上角为 0)
- initialTouchPoint 用户初始触摸坐标(开始动画前)
- lastTouchPoint 用户松手坐标(结束动画前)
- isRightToLeftWhenStart 用户起手是从右向左还是从左向右(即 responseDragWhen 的结果)
- 返回值 翻页行为
- 第一个参数控制 UI 动画,true=从左侧退出,false=从右侧退出
- 第二个参数控制翻页响应,true=下一页,false=上一页,null=不翻页
此回调中的 Point 均为相对于组件左上角的坐标点,且此回调会影响 UI 的呈现和[onTurnPageRequest](#24-翻页请求)回调。
如果不注册此回调,则默认情况下:
- 起手从右向左
- 松手在 x>1/2 处则不翻页
- 松手在 x<1/2 处则翻下一页
- 起手从左向右
- 松手在 x>1/2 处则翻上一页
- 松手在 x<1/2 处则不翻页
[示例代码](https://github.com/FantasticPornTaiQiang/PTQFlipper/blob/main/app/src/main/java/ptq/mpga/pinance/NovelActivity.kt)
---
## 4 更新日志
**v1.0.0**
- 初始版本,readme内的基本功能
**v1.0.1**
- 修复跟随算法bug
**v1.1.0**
- 配置项新增distortionInterval和bezierEdgeDownSampling
- 性能优化
- 移除了minSdk26的限制