永久存储

为了实现文档保存、加载和潜在的实时协作等功能,请以序列化格式存储笔画和关联数据。对于 Ink API,需要手动进行序列化和反序列化。

如需准确恢复笔画,请保存其 BrushStrokeInputBatch

基本序列化

定义一个与 Ink 库对象相对应的序列化对象结构。

使用您偏好的框架(例如 Gson、Moshi、Protobuf 等)对序列化数据进行编码,并使用压缩进行优化。

data class SerializedStroke(  val inputs: SerializedStrokeInputBatch,  val brush: SerializedBrush ) data class SerializedBrush(  val size: Float,  val color: Long,  val epsilon: Float,  val stockBrush: SerializedStockBrush ) enum class SerializedStockBrush {  MARKER_V1,  PRESSURE_PEN_V1,  HIGHLIGHTER_V1 } data class SerializedStrokeInputBatch(  val toolType: SerializedToolType,  val strokeUnitLengthCm: Float,  val inputs: List<SerializedStrokeInput> ) data class SerializedStrokeInput(  val x: Float,  val y: Float,  val timeMillis: Float,  val pressure: Float,  val tiltRadians: Float,  val orientationRadians: Float,  val strokeUnitLengthCm: Float ) enum class SerializedToolType {  STYLUS,  TOUCH,  MOUSE,  UNKNOWN } 
class Converters {  private val gson: Gson = GsonBuilder().create()  companion object {  private val stockBrushToEnumValues =  mapOf(  StockBrushes.markerV1 to SerializedStockBrush.MARKER_V1,  StockBrushes.pressurePenV1 to SerializedStockBrush.PRESSURE_PEN_V1,  StockBrushes.highlighterV1 to SerializedStockBrush.HIGHLIGHTER_V1,  )  private val enumToStockBrush =  stockBrushToEnumValues.entries.associate { (key, value) -> value to key }  }  private fun serializeBrush(brush: Brush): SerializedBrush {  return SerializedBrush(  size = brush.size,  color = brush.colorLong,  epsilon = brush.epsilon,  stockBrush = stockBrushToEnumValues[brush.family] ?: SerializedStockBrush.MARKER_V1,  )  }  private fun serializeStrokeInputBatch(inputs: StrokeInputBatch): SerializedStrokeInputBatch {  val serializedInputs = mutableListOf<SerializedStrokeInput>()  val scratchInput = StrokeInput()  for (i in 0 until inputs.size) {  inputs.populate(i, scratchInput)  serializedInputs.add(  SerializedStrokeInput(  x = scratchInput.x,  y = scratchInput.y,  timeMillis = scratchInput.elapsedTimeMillis.toFloat(),  pressure = scratchInput.pressure,  tiltRadians = scratchInput.tiltRadians,  orientationRadians = scratchInput.orientationRadians,  strokeUnitLengthCm = scratchInput.strokeUnitLengthCm,  )  )  }  val toolType =  when (inputs.getToolType()) {  InputToolType.STYLUS -> SerializedToolType.STYLUS  InputToolType.TOUCH -> SerializedToolType.TOUCH  InputToolType.MOUSE -> SerializedToolType.MOUSE  else -> SerializedToolType.UNKNOWN  }  return SerializedStrokeInputBatch(  toolType = toolType,  strokeUnitLengthCm = inputs.getStrokeUnitLengthCm(),  inputs = serializedInputs,  )  }  private fun deserializeStroke(serializedStroke: SerializedStroke): Stroke? {  val inputs = deserializeStrokeInputBatch(serializedStroke.inputs) ?: return null  val brush = deserializeBrush(serializedStroke.brush) ?: return null  return Stroke(brush = brush, inputs = inputs)  }  private fun deserializeBrush(serializedBrush: SerializedBrush): Brush {  val stockBrushFamily = enumToStockBrush[serializedBrush.stockBrush] ?: StockBrushes.markerV1  return Brush.createWithColorLong(  family = stockBrushFamily,  colorLong = serializedBrush.color,  size = serializedBrush.size,  epsilon = serializedBrush.epsilon,  )  }  private fun deserializeStrokeInputBatch(  serializedBatch: SerializedStrokeInputBatch  ): StrokeInputBatch {  val toolType =  when (serializedBatch.toolType) {  SerializedToolType.STYLUS -> InputToolType.STYLUS  SerializedToolType.TOUCH -> InputToolType.TOUCH  SerializedToolType.MOUSE -> InputToolType.MOUSE  else -> InputToolType.UNKNOWN  }  val batch = MutableStrokeInputBatch()  serializedBatch.inputs.forEach { input ->  batch.addOrThrow(  type = toolType,  x = input.x,  y = input.y,  elapsedTimeMillis = input.timeMillis.toLong(),  pressure = input.pressure,  tiltRadians = input.tiltRadians,  orientationRadians = input.orientationRadians,  )  }  return batch  }  fun serializeStrokeToEntity(stroke: Stroke): StrokeEntity {  val serializedBrush = serializeBrush(stroke.brush)  val serializedInputs = serializeStrokeInputBatch(stroke.inputs)  return StrokeEntity(  brushSize = serializedBrush.size,  brushColor = serializedBrush.color,  brushEpsilon = serializedBrush.epsilon,  stockBrush = serializedBrush.stockBrush,  strokeInputs = gson.toJson(serializedInputs),  )  }  fun deserializeEntityToStroke(entity: StrokeEntity): Stroke {  val serializedBrush =  SerializedBrush(  size = entity.brushSize,  color = entity.brushColor,  epsilon = entity.brushEpsilon,  stockBrush = entity.stockBrush,  )  val serializedInputs =  gson.fromJson(entity.strokeInputs, SerializedStrokeInputBatch::class.java)  val brush = deserializeBrush(serializedBrush)  val inputs = deserializeStrokeInputBatch(serializedInputs)  return Stroke(brush = brush, inputs = inputs)  }  fun brushToString(brush: Brush): String {  val serializedBrush = serializeBrush(brush)  return gson.toJson(serializedBrush)  }  fun stringToBrush(jsonString: String): Brush {  val serializedBrush = gson.fromJson(jsonString, SerializedBrush::class.java)  return deserializeBrush(serializedBrush)  } }