ink-custom-brush-builder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCustom brushes in the Android Ink API are built around the
hierarchy:
BrushFamilyBrushFamily
├── InputModel (BrushFamily.InputModel.SlidingWindowModel or PASSTHROUGH_MODEL)
└── BrushCoat[] (one or more rendering layers)
├── BrushTip (shape: scaleX, scaleY, cornerRounding, rotationDegrees, slantDegrees, pinch)
│ └── BrushBehavior[] (dynamic response to stylus or touch input)
│ └── Tree: TargetNode(input = [Operators](input = SourceNode))
└── BrushPaint (visual appearance)
├── SelfOverlap (ANY, ACCUMULATE, or DISCARD)
├── TextureLayer[] (BrushPaint.TilingTexture or BrushPaint.StampingTexture)
└── BrushPaint.ColorFunction[] (OpacityMultiplier or ReplaceColor)Create a by combining a custom with a Compose ,
, and :
BrushBrushFamilyColorsizeepsilonkotlin
val brush = Brush.createWithComposeColor(
family = myCustomFamily,
color = Color.Black,
size = 10f,
epsilon = 0.1f,
)Version and experimental API requirements :
- Requires
androidx.inkand higher : In1.1.0-alpha03(Stable) and1.0.0-1.1.0-alpha01,alpha02,BrushFamily(...),BrushCoat,BrushTip,BrushPaint, andBrushBehaviornodes wereandroidx.ink.brush.behavior.*(in@RestrictTo(LIBRARY_GROUP)stable, custom brushes can only be loaded from binary proto streams using1.0.0). Starting inBrushFamily.decodeand higher , programmatic custom brush constructors graduated to public API (1.1.0-alpha03,BrushPaint.TilingTexture,BrushPaint.StampingTexture,BrushPaint.ColorFunction, and all behavior nodes).BrushFamily.InputModel.SlidingWindowModel- No
required in@OptInand higher : All public custom brush classes and1.1.0-alpha03ink-storageserialization APIs (BrushFamilyandBrushFamily.encode,decode,AndroidBrushFamilySerialization) are unannotated inBrushFamilyDecodeCallbackand higher (1.1.0-alpha03isExperimentalInkCustomBrushApiin@RestrictTo(LIBRARY_GROUP)and higher, and is only required on1.1.0-alpha05serialization inBrushFamilystable).1.0.0
Android Ink API 中的自定义画笔围绕 层级构建:
BrushFamilyBrushFamily
├── InputModel(BrushFamily.InputModel.SlidingWindowModel 或 PASSTHROUGH_MODEL)
└── BrushCoat[](一个或多个渲染层)
├── BrushTip(形状:scaleX、scaleY、cornerRounding、rotationDegrees、slantDegrees、pinch)
│ └── BrushBehavior[](对触控笔或触摸输入的动态响应)
│ └── 树结构:TargetNode(input = [Operators](input = SourceNode))
└── BrushPaint(视觉外观)
├── SelfOverlap(ANY、ACCUMULATE 或 DISCARD)
├── TextureLayer[](BrushPaint.TilingTexture 或 BrushPaint.StampingTexture)
└── BrushPaint.ColorFunction[](OpacityMultiplier 或 ReplaceColor)通过将自定义 与 Compose 、 和 组合来创建 :
BrushFamilyColorsizeepsilonBrushkotlin
val brush = Brush.createWithComposeColor(
family = myCustomFamily,
color = Color.Black,
size = 10f,
epsilon = 0.1f,
)版本与实验性 API 要求:
- 需要
androidx.ink及更高版本:在1.1.0-alpha03(Stable)以及1.0.0–1.1.0-alpha01中,alpha02、BrushFamily(...)、BrushCoat、BrushTip、BrushPaint和BrushBehavior节点均为androidx.ink.brush.behavior.*(在@RestrictTo(LIBRARY_GROUP)Stable 中,自定义画笔只能通过1.0.0从 binary proto streams 加载)。从BrushFamily.decode及更高版本开始,以编程方式创建自定义画笔的构造函数已正式成为 public API(1.1.0-alpha03、BrushPaint.TilingTexture、BrushPaint.StampingTexture、BrushPaint.ColorFunction以及所有 behavior 节点)。BrushFamily.InputModel.SlidingWindowModel- 在
及更高版本中无需1.1.0-alpha03:所有公开的自定义画笔类以及@OptIn的ink-storage序列化 API(BrushFamily和BrushFamily.encode、decode、AndroidBrushFamilySerialization)在BrushFamilyDecodeCallback及更高版本中均未添加实验性注解(1.1.0-alpha03在ExperimentalInkCustomBrushApi及更高版本中为1.1.0-alpha05,且仅在@RestrictTo(LIBRARY_GROUP)Stable 中的1.0.0序列化时需要使用)。BrushFamily
Workflow and task routing
工作流与任务路由
Follow these conditional instructions based on the requested custom brush task:
- Step 1 (Dependencies) : When setting up an Android project for programmatic custom brushes, ask user permission before editing build files and verify that ,
androidx.ink:ink-brush,androidx.ink:ink-brush-compose, andandroidx.ink:ink-storageuse versionandroidx.ink:ink-nativeloaderor higher in1.1.0-alpha03andgradle/libs.versions.toml.app/build.gradle.kts - Step 2 (Hierarchy, tip shape, paint, and input smoothing) : When defining and multi-coat
BrushFamilylayers, configuringBrushCoatgeometry (BrushTip,scaleX,scaleY,cornerRounding,rotationDegrees,slantDegrees), settingpinchproperties (BrushPaint,SelfOverlap), or smoothing stroke input withColorFunction, you MUST follow brush-structure.md. For baselineBrushFamily.InputModel.SlidingWindowModelcreation orBrushresource loading, review Brush APIs.1.0.0 - Step 3 (Dynamic stylus and touch behaviors) : When making a brush respond dynamically to stylus pressure, tilt, speed, direction, or procedural noise using node trees (
BrushBehavior,SourceNode,DampingNode,NoiseNode,ResponseNode,TargetNode), you MUST follow brush-behaviors.md.PolarTargetNode - Step 4 (Textures and serialization) : When adding or
BrushPaint.TilingTexturelayers, implementingBrushPaint.StampingTexture, or encoding and decoding customTextureBitmapStoredefinitions (BrushFamilyandBrushFamily.encode,decode,AndroidBrushFamilySerialization), you MUST follow textures-and-serialization.md. Inform the user which texture image files (such asBrushFamilyDecodeCallback) to add to their project and register inapp/src/main/res/drawable-nodpi/pencil_grain.png.TextureBitmapStore - Step 5 (Complete brush recipes) : When building a complete pressure pen, calligraphy nib, shading pencil, watercolor wash, or multi-coat shadow brush from scratch, start from the copy-ready implementations in examples.md.
- Compose drawing surface, eraser, or persistence : For Compose drawing surface setup, wet and dry rendering, geometry-based erasing, or Room stroke persistence, suggest the
InProgressStrokesskill.ink-app-builder
请根据请求的自定义画笔任务遵循以下条件式指引:
- 步骤 1(依赖项):在为编程式自定义画笔配置 Android 项目时,在编辑构建文件前征得用户许可,并确认 、
androidx.ink:ink-brush、androidx.ink:ink-brush-compose和androidx.ink:ink-storage在androidx.ink:ink-nativeloader和gradle/libs.versions.toml中使用app/build.gradle.kts或更高版本。1.1.0-alpha03 - 步骤 2(层级、笔尖形状、画笔外观和输入平滑):定义 和多涂层
BrushFamily层、配置BrushCoat几何属性(BrushTip、scaleX、scaleY、cornerRounding、rotationDegrees、slantDegrees)、设置pinch属性(BrushPaint、SelfOverlap),或使用ColorFunction平滑笔画输入时,必须遵循 brush-structure.md。如需了解基础BrushFamily.InputModel.SlidingWindowModel创建方式或Brush资源加载,请参阅 Brush APIs。1.0.0 - 步骤 3(动态触控笔与触摸行为):让画笔通过 节点树(
BrushBehavior、SourceNode、DampingNode、NoiseNode、ResponseNode、TargetNode)动态响应触控笔压力、倾斜、速度、方向或程序化噪声时,必须遵循 brush-behaviors.md。PolarTargetNode - 步骤 4(纹理与序列化):添加 或
BrushPaint.TilingTexture层、实现BrushPaint.StampingTexture,或编码和解码自定义TextureBitmapStore定义(BrushFamily和BrushFamily.encode、decode、AndroidBrushFamilySerialization)时,必须遵循 textures-and-serialization.md。告知用户需要将哪些纹理图像文件(例如BrushFamilyDecodeCallback)添加到项目中,并在app/src/main/res/drawable-nodpi/pencil_grain.png中注册。TextureBitmapStore - 步骤 5(完整画笔配方):从头构建完整的压感笔、书法笔尖、阴影铅笔、水彩晕染或多涂层阴影画笔时,请以 examples.md 中可直接复制的实现为基础。
- Compose 绘图表面、橡皮擦或持久化:对于 Compose 绘图表面设置、湿/干渲染、基于几何形状的擦除或 Room 笔画持久化,建议使用
InProgressStrokesskill。ink-app-builder
Antipatterns
反模式
- NEVER use raw builders to construct brushes---always use the public Kotlin constructors (
ink.proto.*,BrushFamily,BrushCoat,BrushTip,BrushPaintfromBrushBehavior).androidx.ink.brush.* - NEVER manually scale stroke width in a touch listener for pressure or tilt sensitivity---use mapping
BrushBehaviororSourceNode.Source.NORMALIZED_PRESSUREtoTILT_IN_RADIANSorTargetNode.Target.SIZE_MULTIPLIER.WIDTH_MULTIPLIER - NEVER use manual bitmap stamping or
Canvasfor textured brushes---useBitmapShaderorBrushPaint.TilingTexturewith aBrushPaint.StampingTexture.TextureBitmapStore - NEVER use on the final (or only)
BlendMode.SRC_OVER---useTextureLayer,BlendMode.MODULATE,DST_IN,DST_OUT, orSRC_ATOPso edge anti-aliasing andSRC_INbehaviors aren't overridden.OPACITY_MULTIPLIER - NEVER serialize as JSON, Gson, Moshi, or
BrushFamily---always useParcelableandBrushFamily.encodeorBrushFamily.decodefromAndroidBrushFamilySerialization.androidx.ink.storage - NEVER implement manual Bézier (,
quadTo) smoothing ---configurecubicTofor input smoothing andBrushFamily.InputModel.SlidingWindowModelfor behavior smoothing.DampingNode - NEVER use in draw loops for organic stroke wobble---attach a
Random.nextFloatvaried over stroke distance inNoiseNode.BrushBehavior
- 绝不要使用原始的 builder 构建画笔——始终使用来自
ink.proto.*的公开 Kotlin 构造函数(androidx.ink.brush.*、BrushFamily、BrushCoat、BrushTip、BrushPaint)。BrushBehavior - 对于压力或倾斜敏感度,绝不要在 touch listener 中手动缩放笔画宽度——应使用 ,将
BrushBehavior或SourceNode.Source.NORMALIZED_PRESSURE映射到TILT_IN_RADIANS或TargetNode.Target.SIZE_MULTIPLIER。WIDTH_MULTIPLIER - 绝不要为纹理画笔手动使用 bitmap stamping 或
Canvas——应使用BitmapShader或BrushPaint.TilingTexture配合BrushPaint.StampingTexture。TextureBitmapStore - 绝不要在最后一个(或唯一的)上使用
TextureLayer——应使用BlendMode.SRC_OVER、BlendMode.MODULATE、DST_IN、DST_OUT或SRC_ATOP,以免覆盖边缘抗锯齿和SRC_IN行为。OPACITY_MULTIPLIER - 绝不要将 序列化为 JSON、Gson、Moshi 或
BrushFamily——始终使用Parcelable中的androidx.ink.storage和BrushFamily.encode或BrushFamily.decode。AndroidBrushFamilySerialization - 绝不要实现手动 Bézier(、
quadTo)平滑——应配置cubicTo实现输入平滑,并使用BrushFamily.InputModel.SlidingWindowModel实现行为平滑。DampingNode - 绝不要在 draw loop 中使用 制造自然的笔画抖动——应在
Random.nextFloat中附加随笔画距离变化的BrushBehavior。NoiseNode