2025年12月17日,Google 正式发布 Android Ink API 1.0 稳定版,这是一个让触控笔应用开发变得前所未有简单的 Jetpack 库。Google Docs、Google Photos、Chrome PDF、Circle to Search 等明星应用都在使用它!
Ink API 是什么?
背景:为什么需要 Ink API?
用户期望触控笔体验能像在纸上书写一样流畅自然。虽然 Android 早已通过框架级 API 将书写延迟降至惊人的 4 毫秒(几乎无法感知),但开发者仍然面临诸多挑战:
- 🎨 笔触生成算法复杂
- 🖌️ 渲染性能优化困难
- 📐 几何计算(擦除、选择)繁琐
- ⚡ 低延迟实现门槛高
Ink API 应运而生! 它将这些复杂的底层逻辑封装成简洁的 API,让开发者能够专注于打造独特的应用功能。

核心特性
| 特性 | 说明 |
|---|---|
| 🚀 超低延迟 | 基于 Jetpack Graphics Library,提供 4ms 级响应 |
| 🧩 模块化设计 | 按需引入,灵活组合 |
| 🎯 Compose 优先 | 完美适配 Jetpack Compose |
| 📱 广泛兼容 | 支持 Android 5.0 (API 21) 及以上 |
谁在用?
Ink API 已经在多款 Google 核心应用中大规模使用:
- ✅ Google Docs - 文档标注
- ✅ Google Photos - 照片编辑
- ✅ Chrome PDF - PDF 批注
- ✅ Circle to Search - 圈选搜索
- ✅ Pixel Studio - 创意绘画
- ✅ YouTube Effect Maker - 特效制作
Circle to Search 团队反馈:"集成过程非常顺利,一周内就构建了可工作的原型!"
模块架构详解
Ink API 采用 五大核心模块 的架构设计:
scss
┌─────────────────────────────────────────────────────────────┐
│ Ink API │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ Strokes │ Brush │ Geometry │ Rendering │
│ 笔触模块 │ 画笔模块 │ 几何模块 │ 渲染模块 │
├──────────────┴──────────────┴──────────────┴────────────────┤
│ Authoring (实时创作模块) │
└─────────────────────────────────────────────────────────────┘
| 模块 | Gradle 依赖 | 功能说明 |
|---|---|---|
| ink-strokes | ink-strokes |
墨迹输入的数据结构与表示 |
| ink-brush | ink-brush-compose |
声明式定义画笔风格(颜色、粗细、类型) |
| ink-geometry | ink-geometry-compose |
几何操作:擦除、选择、碰撞检测 |
| ink-rendering | ink-rendering |
高效渲染笔触到屏幕 |
| ink-authoring | ink-authoring-compose |
处理实时输入,生成平滑笔触 |
| ink-storage | ink-storage |
笔触序列化与持久化存储 |
快速上手
添加依赖
在 build.gradle.kts 中添加:
kotlin
dependencies {
val ink_version = "1.0.0"
// 核心依赖
implementation("androidx.ink:ink-nativeloader:$ink_version")
implementation("androidx.ink:ink-strokes:$ink_version")
implementation("androidx.ink:ink-rendering:$ink_version")
// Compose 集成
implementation("androidx.ink:ink-authoring-compose:$ink_version")
implementation("androidx.ink:ink-brush-compose:$ink_version")
implementation("androidx.ink:ink-geometry-compose:$ink_version")
// 可选:存储支持
implementation("androidx.ink:ink-storage:$ink_version")
}
创建画笔
kotlin
import androidx.ink.brush.Brush
import androidx.ink.brush.StockBrushes
import androidx.compose.ui.graphics.Color
// 创建一个黑色压感画笔
val blackPen = Brush.createWithComposeColor(
family = StockBrushes.pressurePen(), // 压感笔
colorIntArgb = Color.Black.toArgb(),
size = 5f, // 笔触粗细
epsilon = 0.1f // 精度参数
)
// 创建荧光笔效果
val highlighter = Brush.createWithComposeColor(
family = StockBrushes.highlighter(),
colorIntArgb = Color.Yellow.toArgb(),
size = 20f,
epsilon = 0.1f
)
// 基于现有画笔修改颜色
val redPen = blackPen.copyWithComposeColor(
color = Color.Red.toArgb()
)
StockBrushes 内置画笔类型:
pressurePen()- 压感钢笔highlighter()- 荧光笔marker()- 马克笔pressure()- 通用压感笔
实现绘图画布
kotlin
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Matrix
import androidx.ink.authoring.InProgressStrokes
import androidx.ink.rendering.android.canvas.CanvasStrokeRenderer
import androidx.ink.strokes.Stroke
@Composable
fun InkCanvas(
modifier: Modifier = Modifier
) {
// 已完成的笔触列表
var finishedStrokes by remember { mutableStateOf(listOf<Stroke>()) }
// 当前使用的画笔
var currentBrush by remember { mutableStateOf(blackPen) }
// 笔触渲染器
val strokeRenderer = remember { CanvasStrokeRenderer.create() }
Box(modifier = modifier.fillMaxSize()) {
// 底层 Canvas:渲染已完成的"干"笔触
Canvas(modifier = Modifier.fillMaxSize()) {
finishedStrokes.forEach { stroke ->
strokeRenderer.draw(
stroke = stroke,
canvas = drawContext.canvas.nativeCanvas,
strokeToScreenTransform = Matrix()
)
}
}
// 顶层:实时渲染正在绘制的"湿"笔触
InProgressStrokes(
modifier = Modifier.fillMaxSize(),
defaultBrush = currentBrush,
onStrokesFinished = { newStrokes ->
// 将完成的笔触添加到列表
finishedStrokes = finishedStrokes + newStrokes
}
)
}
}
关键概念:湿墨与干墨
scss
用户绘制中 ──► 湿墨 (InProgressStrokes)
│
▼ 手指/笔抬起
干墨 (Canvas 渲染)
- 湿墨 (Wet Ink) :正在绘制的笔触,由
InProgressStrokes实时渲染 - 干墨 (Dry Ink) :已完成的笔触,通过
CanvasStrokeRenderer渲染到 Canvas

实现橡皮擦功能
kotlin
import androidx.ink.geometry.MutableVec
import androidx.ink.geometry.MutableSegment
import androidx.ink.geometry.MutableParallelogram
import androidx.ink.geometry.AffineTransform
class Eraser(
private val eraserSize: Float = 20f
) {
private var previousPoint: MutableVec? = null
/**
* 擦除与橡皮擦路径相交的笔触
*/
fun eraseAt(
currentX: Float,
currentY: Float,
strokes: MutableList<Stroke>
) {
val prev = previousPoint
previousPoint = MutableVec(currentX, currentY)
// 第一个点,无法形成线段
if (prev == null) return
// 构建擦除路径线段
val segment = MutableSegment(
prev,
MutableVec(currentX, currentY)
)
// 创建带有宽度的擦除区域(平行四边形)
val eraseArea = MutableParallelogram()
.populateFromSegmentAndPadding(segment, eraserSize / 2)
// 移除与擦除区域相交的笔触
strokes.removeAll { stroke ->
stroke.shape.intersects(
eraseArea,
AffineTransform.IDENTITY
)
}
}
fun reset() {
previousPoint = null
}
}
完整示例:带工具栏的绘图应用
kotlin
@Composable
fun DrawingApp() {
var strokes by remember { mutableStateOf(listOf<Stroke>()) }
var currentTool by remember { mutableStateOf(Tool.PEN) }
var brushColor by remember { mutableStateOf(Color.Black) }
var brushSize by remember { mutableStateOf(5f) }
val currentBrush by remember(brushColor, brushSize) {
derivedStateOf {
Brush.createWithComposeColor(
family = StockBrushes.pressurePen(),
colorIntArgb = brushColor.toArgb(),
size = brushSize,
epsilon = 0.1f
)
}
}
Column(modifier = Modifier.fillMaxSize()) {
// 工具栏
ToolBar(
currentTool = currentTool,
onToolChange = { currentTool = it },
currentColor = brushColor,
onColorChange = { brushColor = it },
currentSize = brushSize,
onSizeChange = { brushSize = it },
onClear = { strokes = emptyList() }
)
// 画布
Box(modifier = Modifier.weight(1f)) {
when (currentTool) {
Tool.PEN -> {
InkCanvas(
brush = currentBrush,
finishedStrokes = strokes,
onStrokesFinished = { strokes = strokes + it }
)
}
Tool.ERASER -> {
EraserCanvas(
strokes = strokes.toMutableList(),
onStrokesChanged = { strokes = it }
)
}
}
}
}
}
enum class Tool { PEN, ERASER }

进阶功能
笔触序列化与存储
kotlin
import androidx.ink.storage.StrokeEncoder
import androidx.ink.storage.StrokeDecoder
// 序列化笔触
fun saveStrokes(strokes: List<Stroke>): ByteArray {
return StrokeEncoder.encode(strokes)
}
// 反序列化笔触
fun loadStrokes(data: ByteArray): List<Stroke> {
return StrokeDecoder.decode(data)
}
自定义画笔家族
kotlin
@OptIn(ExperimentalInkCustomBrushApi::class)
fun loadCustomBrush(context: Context): BrushFamily {
// 从 raw 资源加载 gzipped protocol buffer 格式的画笔定义
return context.resources.openRawResource(R.raw.calligraphy).use {
BrushFamily.decode(it)
}
}
坐标系统转换
kotlin
// 支持画布缩放和平移
val transform = Matrix().apply {
scale(zoomLevel, zoomLevel)
translate(offsetX, offsetY)
}
strokeRenderer.draw(
stroke = stroke,
canvas = canvas,
strokeToScreenTransform = transform
)
最佳实践
✅ 推荐做法
- 分离湿墨和干墨层 - 使用
InProgressStrokes处理实时绘制,Canvas处理已完成笔触 - 及时提交笔触 - 在
onStrokesFinished回调中立即处理完成的笔触 - 复用渲染器 - 使用
remember缓存CanvasStrokeRenderer实例 - 合理设置 epsilon - 一般 0.1f 即可满足大多数场景
❌ 避免做法
- 不要在主线程执行大量笔触的几何运算
- 不要频繁创建新的 Brush 对象
- 不要忽略坐标系统转换
版本历程
| 版本 | 日期 | 重要更新 |
|---|---|---|
| 1.0.0 | 2025.12.17 | 🎉 正式版发布 |
| 1.0.0-beta02 | 2025.11.19 | 自定义低延迟形状 API |
| 1.0.0-alpha07 | 2025.10.08 | 荧光笔自重叠参数、平滑算法优化 |
| 1.0.0-alpha05 | 2025.06.18 | Compose 互操作模块 |
| 1.0.0-alpha01 | 2024.10.10 | 首个 Alpha 版本 |
总结
Android Ink API 1.0 的正式发布,标志着触控笔应用开发进入了一个新时代:
- 📉 开发门槛大幅降低 - 无需深入理解底层渲染和几何算法
- ⚡ 性能开箱即用 - 4ms 超低延迟,流畅如纸
- 🧩 模块化按需引入 - 从简单绘图到复杂编辑器都能满足
- 🏢 生产环境验证 - Google 核心应用背书
无论你是想开发笔记应用、创意绘画工具,还是为现有应用添加批注功能,Ink API 都是你的最佳选择!