اصلاح کننده های گرافیکی

علاوه بر Canvas Composable، Compose دارای چندین Modifiers گرافیکی مفید است که به ترسیم محتوای سفارشی کمک می کند. این اصلاح کننده ها مفید هستند زیرا می توان آنها را برای هر ترکیب بندی اعمال کرد.

اصلاح کننده های طراحی

تمام دستورات ترسیم با یک اصلاح کننده طراحی در Compose انجام می شود. سه اصلاح کننده اصلی طراحی در Compose وجود دارد:

اصلاح کننده پایه برای طراحی، drawWithContent است، که در آن می توانید ترتیب ترسیم Composable و دستورات ترسیم صادر شده در داخل اصلاح کننده را تعیین کنید. drawBehind یک بسته بندی مناسب در اطراف drawWithContent است که ترتیب ترسیم را در پشت محتوای قابل ترکیب تنظیم می کند. drawWithCache یا onDrawBehind یا onDrawWithContent را در داخل آن فراخوانی می‌کند - و مکانیزمی برای کش کردن اشیاء ایجاد شده در آنها فراهم می‌کند.

Modifier.drawWithContent : ترتیب ترسیم را انتخاب کنید

Modifier.drawWithContent به شما امکان می دهد عملیات DrawScope قبل یا بعد از محتوای composable اجرا کنید. حتماً drawContent را فراخوانی کنید تا سپس محتوای واقعی composable را رندر کنید. با استفاده از این اصلاح کننده، اگر می خواهید محتوای شما قبل یا بعد از عملیات طراحی سفارشی شما ترسیم شود، می توانید ترتیب عملیات را تعیین کنید.

به عنوان مثال، اگر می‌خواهید یک گرادیان شعاعی در بالای محتوای خود برای ایجاد افکت سوراخ کلید چراغ قوه در رابط کاربری ایجاد کنید، می‌توانید کارهای زیر را انجام دهید:

var pointerOffset by remember {
    mutableStateOf(Offset(0f, 0f))
}
Column(
    modifier = Modifier
        .fillMaxSize()
        .pointerInput("dragging") {
            detectDragGestures { change, dragAmount ->
                pointerOffset += dragAmount
            }
        }
        .onSizeChanged {
            pointerOffset = Offset(it.width / 2f, it.height / 2f)
        }
        .drawWithContent {
            drawContent()
            // draws a fully black area with a small keyhole at pointerOffset that’ll show part of the UI.
            drawRect(
                Brush.radialGradient(
                    listOf(Color.Transparent, Color.Black),
                    center = pointerOffset,
                    radius = 100.dp.toPx(),
                )
            )
        }
) {
    // Your composables here
}

شکل 1 : Modifier.drawWithContent در بالای Composable برای ایجاد یک تجربه UI از نوع چراغ قوه استفاده می شود.

Modifier.drawBehind : ترسیم پشت یک ترکیب بندی

Modifier.drawBehind به شما امکان می دهد عملیات DrawScope در پشت محتوای قابل ترکیبی که روی صفحه ترسیم می شود انجام دهید. اگر به اجرای Canvas نگاهی بیندازید، ممکن است متوجه شوید که این فقط یک بسته بندی مناسب در اطراف Modifier.drawBehind است.

برای کشیدن یک مستطیل گرد پشت Text :

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawBehind {
            drawRoundRect(
                Color(0xFFBBAAEE),
                cornerRadius = CornerRadius(10.dp.toPx())
            )
        }
        .padding(4.dp)
)

که نتیجه زیر را ایجاد می کند:

متن و پس زمینه با استفاده از Modifier.drawBehind ترسیم شده است
شکل 2 : متن و پس زمینه با استفاده از Modifier.drawBehind ترسیم شده است

Modifier.drawWithCache : رسم و کش کردن اشیاء ترسیمی

Modifier.drawWithCache اشیایی را که در داخل آن ایجاد می شود در حافظه پنهان نگه می دارد. تا زمانی که اندازه ناحیه ترسیمی یکسان باشد، یا هر شیء حالتی که خوانده می شود تغییر نکرده باشد، اشیاء در حافظه پنهان ذخیره می شوند. این اصلاح کننده برای بهبود عملکرد فراخوانی های ترسیمی مفید است زیرا از نیاز به تخصیص مجدد اشیاء (مانند: Brush, Shader, Path و غیره) که در ترسیم ایجاد می شوند جلوگیری می کند.

از طرف دیگر، می‌توانید با استفاده از remember ، خارج از اصلاح‌کننده، اشیاء را در حافظه پنهان ذخیره کنید. با این حال، این همیشه امکان پذیر نیست زیرا شما همیشه به ترکیب دسترسی ندارید. اگر اشیاء فقط برای طراحی استفاده شوند، استفاده از drawWithCache می تواند کارایی بیشتری داشته باشد.

به عنوان مثال، اگر یک Brush برای ترسیم یک گرادیان پشت Text ایجاد می‌کنید، با استفاده از drawWithCache ، شی Brush را در حافظه پنهان نگه می‌دارید تا اندازه ناحیه طراحی تغییر کند:

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawWithCache {
            val brush = Brush.linearGradient(
                listOf(
                    Color(0xFF9E82F0),
                    Color(0xFF42A5F5)
                )
            )
            onDrawBehind {
                drawRoundRect(
                    brush,
                    cornerRadius = CornerRadius(10.dp.toPx())
                )
            }
        }
)

کش کردن شی Brush با drawWithCache
شکل 3 : ذخیره شی Brush با drawWithCache

اصلاح کننده های گرافیکی

Modifier.graphicsLayer : اعمال تبدیل به composables

Modifier.graphicsLayer اصلاح‌کننده‌ای است که محتوای ترکیب‌پذیر را به یک لایه ترسیم تبدیل می‌کند. یک لایه چند عملکرد مختلف را ارائه می دهد، مانند:

  • جداسازی دستورالعمل‌های ترسیم آن (شبیه به RenderNode ). دستورالعمل های ترسیم گرفته شده به عنوان بخشی از یک لایه را می توان به طور موثر توسط خط لوله رندر بدون اجرای مجدد کد برنامه دوباره صادر کرد.
  • دگرگونی هایی که برای تمام دستورالعمل های طراحی موجود در یک لایه اعمال می شود.
  • شطرنجی سازی برای قابلیت های ترکیب بندی. هنگامی که یک لایه شطرنجی می شود، دستورالعمل های ترسیم آن اجرا می شود و خروجی در یک بافر خارج از صفحه ثبت می شود. ترکیب چنین بافری برای فریم‌های بعدی سریع‌تر از اجرای دستورالعمل‌های منفرد است، اما زمانی که تبدیل‌هایی مانند مقیاس‌بندی یا چرخش اعمال می‌شود، مانند یک بیت مپ عمل می‌کند.

تحولات

Modifier.graphicsLayer برای دستورالعمل های ترسیم خود جداسازی می کند. به عنوان مثال، با استفاده از Modifier.graphicsLayer می توان تبدیل های مختلفی را اعمال کرد. اینها را می توان متحرک یا تغییر داد بدون اینکه نیازی به اجرای مجدد لامبدای طراحی باشد.

Modifier.graphicsLayer اندازه اندازه گیری شده یا محل قرارگیری کامپوزیتی شما را تغییر نمی دهد، زیرا فقط بر مرحله ترسیم تأثیر می گذارد. این به این معنی است که اگر ترکیب‌بندی شما خارج از محدوده طرح‌بندی خود طراحی شود، ممکن است با دیگران همپوشانی داشته باشد.

تبدیل های زیر را می توان با این اصلاح کننده اعمال کرد:

مقیاس - افزایش اندازه

scaleX و scaleY به ترتیب محتوا را در جهت افقی یا عمودی بزرگ یا کوچک می کنند. مقدار 1.0f نشان دهنده عدم تغییر در مقیاس است، مقدار 0.5f به معنای نیمی از بعد است.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.scaleX = 1.2f
            this.scaleY = 0.8f
        }
)

شکل 4 : scaleX و scaleY به یک Image composable اعمال می شود
ترجمه

translationX و translationY می توان با graphicsLayer تغییر داد، translationX قسمت قابل ترکیب را به چپ یا راست حرکت می دهد. translationY قطعه کامپوزیشن را به بالا یا پایین حرکت می دهد.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.translationX = 100.dp.toPx()
            this.translationY = 10.dp.toPx()
        }
)

شکل 5 : translationX و translationY روی Image با Modifier.graphicsLayer اعمال می شود
چرخش

rotationX برای چرخش افقی، rotationY را برای چرخش عمودی و rotationZ را برای چرخش بر روی محور Z تنظیم کنید (چرخش استاندارد). این مقدار بر حسب درجه (0-360) مشخص می شود.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

شکل 6 : rotationX، rotationY و rotationZ روی Image توسط Modifier.graphicsLayer تنظیم شده است.
اصل و نسب

یک transformOrigin را می توان مشخص کرد. سپس به عنوان نقطه ای که از آن تبدیل ها رخ می دهد استفاده می شود. همه نمونه‌ها تاکنون از TransformOrigin.Center استفاده کرده‌اند که در (0.5f, 0.5f) است. اگر مبدأ را در (0f, 0f) مشخص کنید، تبدیل‌ها از گوشه سمت چپ بالا شروع می‌شوند.

اگر مبدا را با تبدیل rotationZ تغییر دهید، می‌بینید که مورد در بالای سمت چپ قطعه کامپوزیشن می‌چرخد:

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.transformOrigin = TransformOrigin(0f, 0f)
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

شکل 7 : چرخش اعمال شده با TransformOrigin روی 0f, 0f تنظیم شده است

گیره و شکل

Shape طرح کلی را مشخص می‌کند که محتوا وقتی clip = true است. در این مثال، ما دو کادر را برای داشتن دو کلیپ مختلف تنظیم کردیم - یکی با استفاده از متغیر clip graphicsLayer و دیگری با استفاده از بسته بندی مناسب Modifier.clip .

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .size(200.dp)
            .graphicsLayer {
                clip = true
                shape = CircleShape
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }
    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(CircleShape)
            .background(Color(0xFF4DB6AC))
    )
}

محتویات کادر اول (متن عبارت "Hello Compose") به شکل دایره بریده می شود:

کلیپ روی Box composable اعمال شد
شکل 8 : گیره اعمال شده روی جعبه قابل ترکیب

اگر سپس یک translationY روی دایره صورتی بالا اعمال کنید، می بینید که مرزهای Composable همچنان یکسان است، اما دایره زیر دایره پایین (و خارج از مرزهای آن) کشیده می شود.

گیره با translationY و حاشیه قرمز برای طرح کلی اعمال شد
شکل 9 : گیره با translationY و حاشیه قرمز برای طرح کلی اعمال شده است

برای برش دادن قابل ترکیب به ناحیه ای که در آن کشیده شده است، می توانید Modifier.clip(RectangleShape) دیگر را در ابتدای زنجیره اصلاح کننده اضافه کنید. سپس محتوا در داخل محدوده اصلی باقی می ماند.

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .clip(RectangleShape)
            .size(200.dp)
            .border(2.dp, Color.Black)
            .graphicsLayer {
                clip = true
                shape = CircleShape
                translationY = 50.dp.toPx()
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }

    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(RoundedCornerShape(500.dp))
            .background(Color(0xFF4DB6AC))
    )
}

کلیپ در بالای تبدیل graphicsLayer اعمال شد
شکل 10 : کلیپ اعمال شده در بالای تبدیل لایه گرافیکی

آلفا

Modifier.graphicsLayer را می توان برای تنظیم یک alpha (مادرمانی) برای کل لایه استفاده کرد. 1.0f کاملا مات و 0.0f نامرئی است.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "clock",
    modifier = Modifier
        .graphicsLayer {
            this.alpha = 0.5f
        }
)

تصویر با آلفا اعمال شده است
شکل 11 : تصویر با استفاده از آلفا

استراتژی ترکیب

کار با آلفا و شفافیت ممکن است به سادگی تغییر یک مقدار آلفا نباشد. علاوه بر تغییر یک آلفا، گزینه ای برای تنظیم یک CompositingStrategy در یک graphicsLayer نیز وجود دارد. یک CompositingStrategy تعیین می‌کند که چگونه محتوای کامپوزیشن با محتوای دیگری که قبلاً روی صفحه ترسیم شده است ترکیب شود (با هم قرار گیرد).

استراتژی های مختلف عبارتند از:

خودکار (پیش‌فرض)

استراتژی ترکیب با بقیه پارامترهای graphicsLayer تعیین می شود. اگر آلفا کمتر از 1.0f باشد یا RenderEffect تنظیم شده باشد، لایه را به یک بافر خارج از صفحه نمایش می دهد. هر زمان که آلفا کمتر از 1f باشد، یک لایه ترکیبی به طور خودکار ایجاد می شود تا محتویات را رندر کند و سپس این بافر خارج از صفحه را با آلفای مربوطه به مقصد بکشد. تنظیم RenderEffect یا Overscroll همیشه محتوا را بدون توجه به مجموعه CompositingStrategy در یک بافر خارج از صفحه نمایش می دهد.

خارج از صفحه نمایش

محتویات composable همیشه قبل از رندر شدن به مقصد، به یک بافت خارج از صفحه یا bitmap تبدیل می شوند. این برای اعمال عملیات BlendMode برای پوشاندن محتوا و برای عملکرد هنگام ارائه مجموعه های پیچیده دستورالعمل های طراحی مفید است.

نمونه ای از استفاده از CompositingStrategy.Offscreen با BlendModes است. با نگاهی به مثال زیر، فرض کنید می‌خواهید با صدور فرمان ترسیم که از BlendMode.Clear استفاده می‌کند، بخش‌هایی از یک Image قابل ترکیب را حذف کنید. اگر compositingStrategy روی CompositingStrategy.Offscreen تنظیم نکنید، BlendMode با تمام محتویات زیر آن تعامل دارد.

Image(painter = painterResource(id = R.drawable.dog),
   contentDescription = "Dog",
   contentScale = ContentScale.Crop,
   modifier = Modifier
       .size(120.dp)
       .aspectRatio(1f)
       .background(
           Brush.linearGradient(
               listOf(
                   Color(0xFFC5E1A5),
                   Color(0xFF80DEEA)
               )
           )
       )
       .padding(8.dp)
       .graphicsLayer {
           compositingStrategy = CompositingStrategy.Offscreen
       }
       .drawWithCache {
           val path = Path()
           path.addOval(
               Rect(
                   topLeft = Offset.Zero,
                   bottomRight = Offset(size.width, size.height)
               )
           )
           onDrawWithContent {
               clipPath(path) {
                   // this draws the actual image - if you don't call drawContent, it wont
                   // render anything
                   this@onDrawWithContent.drawContent()
               }
               val dotSize = size.width / 8f
               // Clip a white border for the content
               drawCircle(
                   Color.Black,
                   radius = dotSize,
                   center = Offset(
                       x = size.width - dotSize,
                       y = size.height - dotSize
                   ),
                   blendMode = BlendMode.Clear
               )
               // draw the red circle indication
               drawCircle(
                   Color(0xFFEF5350), radius = dotSize * 0.8f,
                   center = Offset(
                       x = size.width - dotSize,
                       y = size.height - dotSize
                   )
               )
           }

       }
)

با تنظیم CompositingStrategy روی Offscreen ، یک بافت خارج از صفحه برای اجرای دستورات ایجاد می کند (استفاده از BlendMode فقط برای محتویات این composable). سپس آن را در بالای آنچه که قبلاً روی صفحه نمایش داده شده است، ارائه می دهد، بدون اینکه بر محتوای قبلاً ترسیم شده تأثیر بگذارد.

Modifier.drawWithContent در تصویری که نشانگر دایره را نشان می دهد، با برنامه BlendMode.Clear داخل
شکل 12 : Modifier.drawWithContent روی تصویری که نشانگر دایره را نشان می دهد، با BlendMode.Clear و CompositingStrategy.Offscreen در داخل برنامه

اگر از CompositingStrategy.Offscreen استفاده نکرده‌اید، نتایج اعمال BlendMode.Clear تمام پیکسل‌های مقصد را پاک می‌کند، صرف‌نظر از آنچه قبلاً تنظیم شده است – بافر رندر پنجره (سیاه) قابل مشاهده است. بسیاری از BlendModes که شامل آلفا هستند، بدون بافر خارج از صفحه، آنطور که انتظار می رود کار نمی کنند. به حلقه سیاه دور نشانگر دایره قرمز توجه کنید:

Modifier.drawWithContent روی تصویری که نشانگر دایره را نشان می دهد، با BlendMode.Clear و بدون مجموعه CompositingStrategy
شکل 13 : Modifier.drawWithContent روی تصویری که نشانگر دایره ای را نشان می دهد، با BlendMode.Clear و بدون مجموعه CompositingStrategy

برای درک بیشتر این موضوع: اگر برنامه یک پس‌زمینه پنجره شفاف داشت و از CompositingStrategy.Offscreen استفاده نمی‌کردید، BlendMode با کل برنامه تعامل خواهد داشت. برای نشان دادن برنامه یا تصویر زمینه زیر، مانند این مثال، همه پیکسل ها را پاک می کند:

بدون CompositingStrategy و با استفاده از BlendMode.Clear با برنامه‌ای که پس‌زمینه پنجره‌ای شفاف دارد. کاغذ دیواری صورتی از طریق ناحیه اطراف دایره وضعیت قرمز نشان داده می شود.
شکل 14 : بدون تنظیم استراتژی ترکیبی و استفاده از BlendMode.Clear با برنامه ای که پس زمینه پنجره شفاف دارد. توجه کنید که چگونه کاغذ دیواری صورتی از طریق ناحیه اطراف دایره وضعیت قرمز نشان داده می شود.

شایان ذکر است که هنگام استفاده از CompositingStrategy.Offscreen ، یک بافت خارج از صفحه که به اندازه ناحیه طراحی است ایجاد می شود و بر روی صفحه نمایش داده می شود. هر دستور ترسیمی که با این استراتژی انجام می شود، به طور پیش فرض در این منطقه بریده می شود. قطعه کد زیر تفاوت‌ها را هنگام استفاده از بافت‌های خارج از صفحه نشان می‌دهد:

@Composable
fun CompositingStrategyExamples() {
   Column(
       modifier = Modifier
           .fillMaxSize()
           .wrapContentSize(Alignment.Center)
   ) {
       /** Does not clip content even with a graphics layer usage here. By default, graphicsLayer
       does not allocate + rasterize content into a separate layer but instead is used
       for isolation. That is draw invalidations made outside of this graphicsLayer will not
       re-record the drawing instructions in this composable as they have not changed **/
       Canvas(
           modifier = Modifier
               .graphicsLayer()
               .size(100.dp) // Note size of 100 dp here
               .border(2.dp, color = Color.Blue)
       ) {
           // ... and drawing a size of 200 dp here outside the bounds
           drawRect(color = Color.Magenta, size = Size(200.dp.toPx(), 200.dp.toPx()))
       }

       Spacer(modifier = Modifier.size(300.dp))

       /** Clips content as alpha usage here creates an offscreen buffer to rasterize content
       into first then draws to the original destination **/
       Canvas(
           modifier = Modifier
               // force to an offscreen buffer
               .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
               .size(100.dp) // Note size of 100 dp here
               .border(2.dp, color = Color.Blue)
       ) {
           /** ... and drawing a size of 200 dp. However, because of the CompositingStrategy.Offscreen usage above, the
           content gets clipped **/
           drawRect(color = Color.Red, size = Size(200.dp.toPx(), 200.dp.toPx()))
       }
   }
}

CompositingStrategy.Auto vs CompositingStrategy.Offscreen - کلیپ‌های خارج از صفحه به منطقه‌ای که خودکار انجام نمی‌شود
شکل 15 : CompositingStrategy.Auto در مقابل CompositingStrategy.Offscreen - کلیپ های خارج از صفحه به منطقه، جایی که خودکار انجام نمی شود
ModulateAlpha

این استراتژی ترکیب، آلفا را برای هر یک از دستورالعمل های ترسیمی که در graphicsLayer ثبت شده اند، تعدیل می کند. تا زمانی که RenderEffect تنظیم نشده باشد، بافر خارج از صفحه برای آلفا زیر 1.0f ایجاد نمی کند، بنابراین می تواند برای رندر آلفا کارآمدتر باشد. با این حال، می تواند نتایج متفاوتی را برای محتوای همپوشانی ارائه دهد. برای موارد استفاده که از قبل مشخص شده است که محتوا همپوشانی ندارد، این می تواند عملکرد بهتری نسبت به CompositingStrategy.Auto با مقادیر آلفای کمتر از 1 ارائه دهد.

مثال دیگری از استراتژی‌های ترکیب‌بندی مختلف در زیر است - اعمال آلفاهای مختلف در بخش‌های مختلف ترکیب‌پذیرها و اعمال استراتژی Modulate :

@Preview
@Composable
fun CompositingStratgey_ModulateAlpha() {
  Column(
      modifier = Modifier
          .fillMaxSize()
          .padding(32.dp)
  ) {
      // Base drawing, no alpha applied
      Canvas(
          modifier = Modifier.size(200.dp)
      ) {
          drawSquares()
      }

      Spacer(modifier = Modifier.size(36.dp))

      // Alpha 0.5f applied to whole composable
      Canvas(modifier = Modifier
          .size(200.dp)
          .graphicsLayer {
              alpha = 0.5f
          }) {
          drawSquares()
      }
      Spacer(modifier = Modifier.size(36.dp))

      // 0.75f alpha applied to each draw call when using ModulateAlpha
      Canvas(modifier = Modifier
          .size(200.dp)
          .graphicsLayer {
              compositingStrategy = CompositingStrategy.ModulateAlpha
              alpha = 0.75f
          }) {
          drawSquares()
      }
  }
}

private fun DrawScope.drawSquares() {

  val size = Size(100.dp.toPx(), 100.dp.toPx())
  drawRect(color = Red, size = size)
  drawRect(
      color = Purple, size = size,
      topLeft = Offset(size.width / 4f, size.height / 4f)
  )
  drawRect(
      color = Yellow, size = size,
      topLeft = Offset(size.width / 4f * 2f, size.height / 4f * 2f)
  )
}

val Purple = Color(0xFF7E57C2)
val Yellow = Color(0xFFFFCA28)
val Red = Color(0xFFEF5350)

ModulateAlpha مجموعه آلفا را برای هر فرمان ترسیم جداگانه اعمال می کند
شکل 16 : ModulateAlpha مجموعه آلفا را برای هر دستور ترسیم به کار می برد

محتویات یک Composable را در یک بیت مپ بنویسید

یک مورد معمول استفاده، ایجاد یک Bitmap از یک Composable است. برای کپی کردن محتویات composable خود در Bitmap ، یک GraphicsLayer با استفاده از rememberGraphicsLayer() ایجاد کنید.

دستورات ترسیم را با استفاده از drawWithContent() و graphicsLayer.record{} به لایه جدید هدایت کنید. سپس با استفاده از drawLayer لایه را در بوم قابل مشاهده بکشید:

val coroutineScope = rememberCoroutineScope()
val graphicsLayer = rememberGraphicsLayer()
Box(
    modifier = Modifier
        .drawWithContent {
            // call record to capture the content in the graphics layer
            graphicsLayer.record {
                // draw the contents of the composable into the graphics layer
                this@drawWithContent.drawContent()
            }
            // draw the graphics layer on the visible canvas
            drawLayer(graphicsLayer)
        }
        .clickable {
            coroutineScope.launch {
                val bitmap = graphicsLayer.toImageBitmap()
                // do something with the newly acquired bitmap
            }
        }
        .background(Color.White)
) {
    Text("Hello Android", fontSize = 26.sp)
}

می توانید بیت مپ را روی دیسک ذخیره کرده و به اشتراک بگذارید. برای جزئیات بیشتر، نمونه کامل نمونه را ببینید. قبل از ذخیره روی دیسک، حتماً مجوزهای دستگاه را بررسی کنید.

اصلاح کننده طراحی سفارشی

برای ایجاد اصلاح کننده سفارشی خود، رابط DrawModifier را پیاده سازی کنید. این به شما امکان دسترسی به ContentDrawScope را می‌دهد، که همان چیزی است که هنگام استفاده از Modifier.drawWithContent() در معرض نمایش قرار می‌گیرد. سپس می‌توانید عملیات ترسیم رایج را به اصلاح‌کننده‌های طراحی سفارشی استخراج کنید تا کد را پاک کنید و بسته‌بندی‌های مناسب تهیه کنید. برای مثال، Modifier.background() یک DrawModifier مناسب است.

به عنوان مثال، اگر می‌خواهید یک Modifier پیاده‌سازی کنید که محتوا را به صورت عمودی برگرداند، می‌توانید به صورت زیر ایجاد کنید:

class FlippedModifier : DrawModifier {
    override fun ContentDrawScope.draw() {
        scale(1f, -1f) {
            this@draw.drawContent()
        }
    }
}

fun Modifier.flipped() = this.then(FlippedModifier())

سپس از این اصلاح کننده برگردان اعمال شده روی Text استفاده کنید:

Text(
    "Hello Compose!",
    modifier = Modifier
        .flipped()
)

تغییر دهنده سفارشی بر روی متن
شکل 17 : تعدیل کننده سفارشی بر روی متن

منابع اضافی

برای مثال‌های بیشتر با استفاده از graphicsLayer و طراحی سفارشی، منابع زیر را بررسی کنید:

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}