রাষ্ট্রীয় সংরক্ষণ এবং অবিরাম সঞ্চয়স্থান হল কালি অ্যাপের অ-তুচ্ছ দিক, বিশেষ করে রচনায়। মূল ডেটা অবজেক্ট, যেমন ব্রাশের বৈশিষ্ট্য এবং যে পয়েন্টগুলি একটি স্ট্রোক তৈরি করে, সেগুলো জটিল এবং স্বয়ংক্রিয়ভাবে স্থায়ী হয় না। কনফিগারেশন পরিবর্তন এবং ডাটাবেসে ব্যবহারকারীর অঙ্কন স্থায়ীভাবে সংরক্ষণ করার মতো পরিস্থিতির সময় অবস্থা সংরক্ষণের জন্য এর জন্য একটি ইচ্ছাকৃত কৌশল প্রয়োজন।
রাষ্ট্রীয় সংরক্ষণ
জেটপ্যাক কম্পোজে, ইউআই স্টেট সাধারণত remember
এবং rememberSaveable
ব্যবহার করে পরিচালিত হয়। যদিও rememberSaveable
কনফিগারেশন পরিবর্তনগুলি জুড়ে স্বয়ংক্রিয় অবস্থা সংরক্ষণের প্রস্তাব দেয়, এর অন্তর্নির্মিত ক্ষমতাগুলি আদিম ডেটা প্রকার এবং অবজেক্টের মধ্যে সীমাবদ্ধ যা Parcelable
বা Serializable
প্রয়োগ করে।
কাস্টম অবজেক্টের জন্য যা জটিল বৈশিষ্ট্যগুলি ধারণ করে, যেমন Brush
, আপনাকে স্পষ্ট সিরিয়ালাইজেশন এবং ডিসিরিয়ালাইজেশন মেকানিজম সংজ্ঞায়িত করতে হবে। একটি কাস্টম স্টেট সেভার এটির জন্য দরকারী। Brush
অবজেক্টের জন্য একটি কাস্টম Saver
সংজ্ঞায়িত করে, আপনি যখন কনফিগারেশন পরিবর্তনগুলি ঘটবে তখন আপনি এর প্রয়োজনীয় বৈশিষ্ট্যগুলি সংরক্ষণ করতে পারেন, যেমনটি নিম্নলিখিত brushStateSaver
উদাহরণে দেখানো হয়েছে।
fun brushStateSaver(converters: Converters): Saver<MutableState<Brush>, String> = Saver(
save = { state ->
converters.brushToString(state.value)
},
restore = { jsonString ->
val brush = converters.stringToBrush(jsonString)
mutableStateOf(brush)
}
)
তারপরে আপনি নির্বাচিত ব্রাশের অবস্থা সংরক্ষণ করতে কাস্টম Saver
ব্যবহার করতে পারেন:
val converters = Converters()
val currentBrush = rememberSaveable(saver = brushStateSaver(converters)) { mutableStateOf(defaultBrush) }
ক্রমাগত স্টোরেজ
নথি সংরক্ষণ, লোডিং এবং সম্ভাব্য রিয়েল-টাইম সহযোগিতার মতো বৈশিষ্ট্যগুলি সক্ষম করতে, একটি সিরিয়ালাইজড বিন্যাসে স্ট্রোক এবং সম্পর্কিত ডেটা সংরক্ষণ করুন। কালি API-এর জন্য, ম্যানুয়াল সিরিয়ালাইজেশন এবং ডিসিরিয়ালাইজেশন প্রয়োজন।
একটি স্ট্রোক সঠিকভাবে পুনরুদ্ধার করতে, এটির Brush
এবং StrokeInputBatch
সংরক্ষণ করুন।
-
Brush
: সাংখ্যিক ক্ষেত্র (আকার, এপসিলন), রঙ এবংBrushFamily
অন্তর্ভুক্ত করে। -
StrokeInputBatch
: সাংখ্যিক ক্ষেত্র সহ ইনপুট পয়েন্টগুলির একটি তালিকা।
মৌলিক সিরিয়ালাইজেশন
একটি সিরিয়ালাইজেশন অবজেক্ট স্ট্রাকচার সংজ্ঞায়িত করুন যা ইঙ্ক লাইব্রেরি অবজেক্টগুলিকে মিরর করে।
আপনার পছন্দের ফ্রেমওয়ার্ক যেমন 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)
}
}