حفظ وضعیت و ذخیرهسازی مداوم، جنبههای مهم و غیرقابل انکار برنامههای inking هستند. این امر مستلزم یک استراتژی آگاهانه برای ذخیره وضعیت در سناریوهایی مانند تغییرات پیکربندی و ذخیره دائمی ترسیمهای کاربر در پایگاه داده است.
حفظ ایالت
در برنامههای مبتنی بر نما، وضعیت رابط کاربری با استفاده از ترکیبی از موارد زیر مدیریت میشود:
- اشیاء
ViewModel - حالت نمونه ذخیره شده با استفاده از:
- فعالیت
onSaveInstanceState() - ViewModel ذخیره شده حالت کنترل
- ذخیرهسازی محلی برای حفظ وضعیت رابط کاربری در طول انتقال برنامه و فعالیت
- فعالیت
به ذخیره حالتهای رابط کاربری مراجعه کنید.
ذخیرهسازی پایدار
برای فعال کردن ویژگیهایی مانند ذخیره، بارگذاری و همکاری بالقوه در لحظه سند، strokeها و دادههای مرتبط را در قالب سریالی ذخیره کنید. برای Ink API، سریالسازی و deserialization دستی ضروری است.
برای بازیابی دقیق یک stroke، Brush و StrokeInputBatch آن را ذخیره کنید.
-
Brush): شامل فیلدهای عددی (اندازه، اپسیلون)، رنگ وBrushFamilyاست. -
StrokeInputBatch: فهرستی از نقاط ورودی با فیلدهای عددی.
ماژول Storage، سریالسازی فشرده پیچیدهترین بخش یعنی StrokeInputBatch ساده میکند.
برای نجات از سکته مغزی:
- با استفاده از تابع encode ماژول ذخیرهسازی،
StrokeInputBatchرا سریالایز کنید. دادههای دودویی حاصل را ذخیره کنید. - ویژگیهای ضروری قلممو مربوط به خط دور را جداگانه ذخیره کنید:
- شمارشی که خانواده قلممو را نشان میدهد &mdash اگرچه میتوان نمونه را سریالسازی کرد، اما این برای برنامههایی که از انتخاب محدودی از خانوادههای قلممو استفاده میکنند، کارآمد نیست.
-
colorLong -
size -
epsilon
fun serializeStroke(stroke: Stroke): SerializedStroke {
val serializedBrush = serializeBrush(stroke.brush)
val encodedSerializedInputs = ByteArrayOutputStream().use
{
stroke.inputs.encode(it)
it.toByteArray()
}
return SerializedStroke(
inputs = encodedSerializedInputs,
brush = serializedBrush
)
}
برای بارگذاری یک شیء stroke:
- دادههای دودویی ذخیره شده برای
StrokeInputBatchرا بازیابی کرده و با استفاده از تابع decode() ماژول ذخیرهسازی، آن را deserialize کنید. - ویژگیهای ذخیرهشدهی
Brushرا بازیابی کنید و قلممو را ایجاد کنید. با استفاده از قلممو بازسازیشده و
StrokeInputBatchاز حالت سریال خارجشده، stroke نهایی را ایجاد کنید.fun deserializeStroke(serializedStroke: SerializedStroke): Stroke { val inputs = ByteArrayInputStream(serializedStroke.inputs).use { StrokeInputBatch.decode(it) } val brush = deserializeBrush(serializedStroke.brush) return Stroke(brush = brush, inputs = inputs) }
کنترل زوم، پن و چرخش
اگر برنامه شما از بزرگنمایی، حرکت افقی یا چرخش پشتیبانی میکند، باید تبدیل فعلی را به InProgressStrokes ارائه دهید. این به ترسیم خطوط جدید کمک میکند تا موقعیت و مقیاس خطوط موجود شما را مطابقت دهند.
شما این کار را با ارسال یک Matrix به پارامتر pointerEventToWorldTransform انجام میدهید. این ماتریس باید معکوس تبدیلی را که روی بوم stroke های نهایی خود اعمال میکنید، نشان دهد.
@Composable
fun ZoomableDrawingScreen(...) {
// 1. Manage your zoom/pan state (e.g., using detectTransformGestures).
var zoom by remember { mutableStateOf(1f) }
var pan by remember { mutableStateOf(Offset.Zero) }
// 2. Create the Matrix.
val pointerEventToWorldTransform = remember(zoom, pan) {
android.graphics.Matrix().apply {
// Apply the inverse of your rendering transforms
postTranslate(-pan.x, -pan.y)
postScale(1 / zoom, 1 / zoom)
}
}
Box(modifier = Modifier.fillMaxSize()) {
// ...Your finished strokes Canvas, with regular transform applied
// 3. Pass the matrix to InProgressStrokes.
InProgressStrokes(
modifier = Modifier.fillMaxSize(),
pointerEventToWorldTransform = pointerEventToWorldTransform,
defaultBrush = currentBrush,
nextBrush = onGetNextBrush,
onStrokesFinished = onStrokesFinished
)
}
}
سکته مغزی صادراتی
ممکن است لازم باشد صحنهی طراحی خود را به عنوان یک فایل تصویری ثابت خروجی بگیرید. این کار برای اشتراکگذاری طراحی با سایر برنامهها، ایجاد تصاویر بندانگشتی یا ذخیرهی نسخهی نهایی و غیرقابل ویرایش محتوا مفید است.
برای خروجی گرفتن از یک صحنه، میتوانید خطوط ترسیمی خود را به جای اینکه مستقیماً روی صفحه نمایش داده شوند، روی یک بیتمپ خارج از صفحه نمایش رندر کنید. از Android's Picture API ) استفاده کنید که به شما امکان میدهد بدون نیاز به یک کامپوننت رابط کاربری قابل مشاهده، نقاشیها را روی بوم ضبط کنید.
این فرآیند شامل ایجاد یک نمونه Picture ، فراخوانی beginRecording() برای دریافت یک Canvas و سپس استفاده از CanvasStrokeRenderer موجود برای ترسیم هر stroke روی آن Canvas . پس از ثبت تمام دستورات ترسیم، میتوانید از Picture برای ایجاد یک Bitmap استفاده کنید که میتوانید آن را فشرده کرده و در یک فایل ذخیره کنید.
fun exportDocumentAsImage() {
val picture = Picture()
val canvas = picture.beginRecording(bitmapWidth, bitmapHeight)
// The following is similar logic that you'd use in your custom View.onDraw or Compose Canvas.
for (item in myDocument) {
when (item) {
is Stroke -> {
canvasStrokeRenderer.draw(canvas, stroke, worldToScreenTransform)
}
// Draw your other types of items to the canvas.
}
}
// Create a Bitmap from the Picture and write it to a file.
val bitmap = Bitmap.createBitmap(picture)
val outstream = FileOutputStream(filename)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outstream)
}
کمککنندههای شیء داده و مبدل
یک ساختار شیء سریالسازی تعریف کنید که اشیاء API مورد نیاز Ink را منعکس کند.
از ماژول ذخیرهسازی Ink API برای رمزگذاری و رمزگشایی StrokeInputBatch استفاده کنید.
اشیاء انتقال داده
@Parcelize
@Serializable
data class SerializedStroke(
val inputs: ByteArray,
val brush: SerializedBrush
) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SerializedStroke) return false
if (!inputs.contentEquals(other.inputs)) return false
if (brush != other.brush) return false
return true
}
override fun hashCode(): Int {
var result = inputs.contentHashCode()
result = 31 * result + brush.hashCode()
return result
}
}
@Parcelize
@Serializable
data class SerializedBrush(
val size: Float,
val color: Long,
val epsilon: Float,
val stockBrush: SerializedStockBrush,
val clientBrushFamilyId: String? = null
) : Parcelable
enum class SerializedStockBrush {
Marker,
PressurePen,
Highlighter,
DashedLine,
}
مبدلها
object Converters {
private val stockBrushToEnumValues = mapOf(
StockBrushes.marker() to SerializedStockBrush.Marker,
StockBrushes.pressurePen() to SerializedStockBrush.PressurePen,
StockBrushes.highlighter() to SerializedStockBrush.Highlighter,
StockBrushes.dashedLine() to SerializedStockBrush.DashedLine,
)
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,
)
}
fun serializeStroke(stroke: Stroke): SerializedStroke {
val serializedBrush = serializeBrush(stroke.brush)
val encodedSerializedInputs = ByteArrayOutputStream().use { outputStream ->
stroke.inputs.encode(outputStream)
outputStream.toByteArray()
}
return SerializedStroke(
inputs = encodedSerializedInputs,
brush = serializedBrush
)
}
private fun deserializeStroke(
serializedStroke: SerializedStroke,
): Stroke? {
val inputs = ByteArrayInputStream(serializedStroke.inputs).use { inputStream ->
StrokeInputBatch.decode(inputStream)
}
val brush = deserializeBrush(serializedStroke.brush, customBrushes)
return Stroke(brush = brush, inputs = inputs)
}
private fun deserializeBrush(
serializedBrush: SerializedBrush,
): Brush {
val stockBrushFamily = enumToStockBrush[serializedBrush.stockBrush]
val brushFamily = customBrush?.brushFamily ?: stockBrushFamily ?: StockBrushes.marker()
return Brush.createWithColorLong(
family = brushFamily,
colorLong = serializedBrush.color,
size = serializedBrush.size,
epsilon = serializedBrush.epsilon,
)
}
}