Compose, Canvas
composable'a ek olarak özel içerik çizmeye yardımcı olan çeşitli faydalı grafiklere Modifiers
sahiptir. Bu değiştiriciler, herhangi bir composable'a uygulanabildikleri için kullanışlıdır.
Çizim değiştiriciler
Tüm çizim komutları, Compose'da çizim değiştiriciyle yapılır. Oluştur'da üç ana çizim değiştirici vardır:
Çizim için temel değiştirici drawWithContent
'dır. Burada, Composable'ınızın çizim sırasına ve değiştiricinin içinde verilen çizim komutlarına karar verebilirsiniz. drawBehind
, drawWithContent
için uygun bir sarmalayıcıdır. drawWithContent
'nin çizim sırası, composable'ın içeriğinin arkasında olacak şekilde ayarlanmıştır. drawWithCache
içinde onDrawBehind
veya onDrawWithContent
çağrıları yapar ve bunlarda oluşturulan nesneleri önbelleğe almak için bir mekanizma sağlar.
Modifier.drawWithContent
: Çizim sırasını seçin
Modifier.drawWithContent
, DrawScope
işlemlerini composable'ın içeriğinden önce veya sonra yürütmenize olanak tanır. Daha sonra composable'ın gerçek içeriğini oluşturmak için drawContent
işlevini çağırdığınızdan emin olun. Bu değiştiriciyle, içeriklerinizin özel çizim işlemlerinizden önce mi yoksa sonra mı çizilmesini istediğinize karar vererek işlemlerin sırasını belirleyebilirsiniz.
Örneğin, kullanıcı arayüzünde el feneri anahtar deliği efekti oluşturmak için içeriğinizin üzerinde radyal bir gradyan oluşturmak istiyorsanız aşağıdakileri yapabilirsiniz:
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 }
Modifier.drawBehind
: Bir composable'ın arkasında çizim yapma
Modifier.drawBehind
, ekranda çizilen composable içeriğin arkasında DrawScope
işlemlerini gerçekleştirmenize olanak tanır. Canvas
uygulamasını incelerseniz bunun yalnızca Modifier.drawBehind
için uygun bir sarmalayıcı olduğunu fark edebilirsiniz.
Text
simgesinin arkasına yuvarlatılmış bir dikdörtgen çizmek için:
Text( "Hello Compose!", modifier = Modifier .drawBehind { drawRoundRect( Color(0xFFBBAAEE), cornerRadius = CornerRadius(10.dp.toPx()) ) } .padding(4.dp) )
Bu işlem aşağıdaki sonucu verir:

Modifier.drawWithCache
: Çizim nesnelerini çizme ve önbelleğe alma
Modifier.drawWithCache
, içinde oluşturulan nesneleri önbelleğe alır. Çizim alanının boyutu aynı olduğu veya okunan durum nesneleri değişmediği sürece nesneler önbelleğe alınır. Bu değiştirici, çizim üzerinde oluşturulan nesnelerin (ör. Brush, Shader, Path
vb.) yeniden ayrılmasına gerek kalmadığı için çizim çağrılarının performansını artırmak için kullanışlıdır.
Alternatif olarak, değiştiricinin dışında remember
kullanarak da nesneleri önbelleğe alabilirsiniz. Ancak kompozisyona her zaman erişiminiz olmadığından bu her zaman mümkün değildir. Nesneler yalnızca çizim için kullanılıyorsa drawWithCache
kullanmak daha iyi performans sağlayabilir.
Örneğin, Text
öğesinin arkasına gradyan çizmek için Brush
oluşturursanız drawWithCache
, çizim alanının boyutu değişene kadar Brush
nesnesini önbelleğe alır:
Text( "Hello Compose!", modifier = Modifier .drawWithCache { val brush = Brush.linearGradient( listOf( Color(0xFF9E82F0), Color(0xFF42A5F5) ) ) onDrawBehind { drawRoundRect( brush, cornerRadius = CornerRadius(10.dp.toPx()) ) } } )

Grafik değiştiriciler
Modifier.graphicsLayer
: Composables'a dönüşüm uygulama
Modifier.graphicsLayer
Birleştirilebilir öğenin içeriğini çizim katmanına dönüştüren bir değiştiricidir. Bir katman, aşağıdakiler gibi birkaç farklı işlev sunar:
- Çizim talimatları için yalıtım (
RenderNode
'a benzer). Bir katman kapsamında yakalanan çizim talimatları, uygulama kodu yeniden yürütülmeden oluşturma hattı tarafından verimli bir şekilde yeniden verilebilir. - Bir katmanda bulunan tüm çizim talimatlarına uygulanan dönüşümler.
- Beste özelliklerinde rasterleştirme. Bir katman rasterleştirildiğinde çizim talimatları yürütülür ve çıkış, ekran dışı bir arabelleğe kaydedilir. Sonraki kareler için böyle bir birleştirme işlemi, tek tek talimatları yürütmekten daha hızlıdır ancak ölçeklendirme veya döndürme gibi dönüşümler uygulandığında bit eşlem olarak davranır.
Dönüşümler
Modifier.graphicsLayer
, çizim talimatları için izolasyon sağlar. Örneğin, Modifier.graphicsLayer
kullanılarak çeşitli dönüşümler uygulanabilir.
Bunlar, çizim lambda'sını yeniden yürütmeye gerek kalmadan animasyonlu hale getirilebilir veya değiştirilebilir.
Modifier.graphicsLayer
yalnızca çizim aşamasını etkilediğinden, birleştirilebilir öğenizin ölçülen boyutunu veya yerleşimini değiştirmez. Bu, düzen sınırlarının dışında çizim yaparsa composable'ınızın diğerleriyle çakışabileceği anlamına gelir.
Bu değiştiriciyle aşağıdaki dönüşümler uygulanabilir:
Ölçek - boyutu büyütme
scaleX
ve scaleY
, içeriği sırasıyla yatay veya dikey yönde büyütür ya da küçültür. 1.0f
değeri ölçekte değişiklik olmadığını, 0.5f
değeri ise boyutun yarısı olduğunu gösterir.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "Sunset", modifier = Modifier .graphicsLayer { this.scaleX = 1.2f this.scaleY = 0.8f } )
Çeviri
translationX
ve translationY
, graphicsLayer
ile değiştirilebilir.
translationX
, composable'ı sola veya sağa taşır. translationY
, composable'ı yukarı veya aşağı taşır.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "Sunset", modifier = Modifier .graphicsLayer { this.translationX = 100.dp.toPx() this.translationY = 10.dp.toPx() } )
Döndürme
rotationX
seçeneğini yatay döndürme, rotationY
seçeneğini dikey döndürme ve rotationZ
seçeneğini Z ekseninde döndürme (standart döndürme) olarak ayarlayın. Bu değer, derece (0-360) olarak belirtilir.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "Sunset", modifier = Modifier .graphicsLayer { this.rotationX = 90f this.rotationY = 275f this.rotationZ = 180f } )
Köken
transformOrigin
belirtilebilir. Ardından, dönüşümlerin gerçekleştiği nokta olarak kullanılır. Şimdiye kadarki tüm örneklerde TransformOrigin.Center
kullanıldı. Bu, (0.5f, 0.5f)
konumundadır. Başlangıç noktasını (0f, 0f)
olarak belirtirseniz dönüşümler, birleştirilebilir öğenin sol üst köşesinden başlar.
Kaynak noktayı rotationZ
dönüşümüyle değiştirirseniz öğenin, composable'ın sol üst kısmı etrafında döndüğünü görebilirsiniz:
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 } )
Klip ve Şekil
Şekil, içerik clip = true
olduğunda kırpılacağı ana hattı belirtir. Bu örnekte, iki kutuyu iki farklı klip içerecek şekilde ayarlıyoruz. Biri graphicsLayer
klip değişkenini, diğeri ise kullanışlı sarmalayıcı Modifier.clip
'yi kullanıyor.
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)) ) }
İlk kutunun içeriği (metin "Hello Compose") daire şekliyle kırpılıyor:

Ardından üstteki pembe daireye translationY
uygularsanız Composable'ın sınırlarının aynı kaldığını ancak dairenin alttaki dairenin altında (ve sınırlarının dışında) çizildiğini görürsünüz.

Birleştirilebilir öğeyi çizildiği bölgeye kırpmak için değiştirici zincirinin başına başka bir Modifier.clip(RectangleShape)
ekleyebilirsiniz. İçerik daha sonra orijinal sınırlar içinde kalır.
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)) ) }

Alfa
Modifier.graphicsLayer
, katmanın tamamı için alpha
(opaklık) ayarlamak üzere kullanılabilir. 1.0f
tamamen opak, 0.0f
ise görünmezdir.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "clock", modifier = Modifier .graphicsLayer { this.alpha = 0.5f } )

Birleştirme stratejisi
Alfa ve şeffaflıkla çalışmak, tek bir alfa değerini değiştirmek kadar basit olmayabilir. Alfa değerini değiştirmenin yanı sıra graphicsLayer
üzerinde CompositingStrategy
ayarlama seçeneği de vardır. CompositingStrategy
, composable'ın içeriğinin ekranda zaten çizilmiş olan diğer içeriklerle nasıl birleştirileceğini (bir araya getirileceğini) belirler.
Farklı stratejiler şunlardır:
Otomatik (varsayılan)
Birleştirme stratejisi, graphicsLayer
parametrelerin geri kalanı tarafından belirlenir. Alfa değeri 1,0f'ten küçükse veya RenderEffect
ayarlanmışsa katmanı ekran dışı bir arabelleğe işler. Alfa değeri 1f'den küçük olduğunda içerikleri oluşturmak için otomatik olarak bir birleştirme katmanı oluşturulur ve bu ekran dışı arabellek, ilgili alfa değeriyle birlikte hedefe çizilir. RenderEffect
veya aşırı kaydırma ayarlandığında, CompositingStrategy
ayarından bağımsız olarak içerik her zaman ekran dışı bir arabelleğe oluşturulur.
Ekran dışında
Composable'ın içeriği, hedefte oluşturulmadan önce her zaman ekran dışı bir doku veya bit eşleme olarak rasterleştirilir. Bu, içeriği maskelemek için BlendMode
işlemlerini uygulamak ve karmaşık çizim talimatlarını oluştururken performansı artırmak için kullanışlıdır.
CompositingStrategy.Offscreen
kullanımına örnek olarak BlendModes
verilebilir. Aşağıdaki örneğe göz atalım:
BlendMode.Clear
kullanarak bir çizim komutu vererek Image
composable'ın bazı bölümlerini kaldırmak istediğinizi varsayalım. compositingStrategy
değerini CompositingStrategy.Offscreen
olarak ayarlamazsanız BlendMode
, altındaki tüm içeriklerle etkileşime girer.
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
değerini Offscreen
olarak ayarlayarak komutları yürütmek için ekran dışı bir doku oluşturur (BlendMode
yalnızca bu composable'ın içeriğine uygulanır). Ardından, ekranda zaten oluşturulmuş olan içeriğin üzerinde oluşturulur ve önceden çizilmiş içeriği etkilemez.

CompositingStrategy.Offscreen
kullanmadıysanız BlendMode.Clear
uygulandığında, hedefteki tüm pikseller temizlenir. Bu işlem, önceden ayarlanmış olanlardan bağımsız olarak pencerenin oluşturma arabelleğini (siyah) görünür bırakır. Alfa içeren birçok BlendModes
, ekran dışı arabellek olmadan beklendiği gibi çalışmaz. Kırmızı daire göstergesinin etrafındaki siyah halkaya dikkat edin:

Bu durumu biraz daha iyi anlamak için: Uygulamanın yarı saydam bir pencere arka planı varsa ve CompositingStrategy.Offscreen
kullanmadıysanız BlendMode
, uygulamanın tamamıyla etkileşime girer. Aşağıdaki örnekte olduğu gibi, altındaki uygulamayı veya duvar kağıdını göstermek için tüm pikselleri temizler:

CompositingStrategy.Offscreen
kullanılırken çizim alanının boyutunda bir ekran dışı doku oluşturulup ekranda tekrar oluşturulduğunu belirtmekte fayda var. Bu stratejiyle yapılan tüm çizim komutları varsayılan olarak bu bölgeye kırpılır. Aşağıdaki kod snippet'i, ekran dışı dokulara geçiş yaparken oluşan farkları gösterir:
@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())) } } }

ModulateAlpha
Bu oluşturma stratejisi, graphicsLayer
içinde kaydedilen her çizim talimatı için alfa değerini düzenler. RenderEffect
ayarlanmadığı sürece 1,0f'nin altındaki alfa için ekran dışı arabellek oluşturmaz.Bu nedenle, alfa oluşturma için daha verimli olabilir. Ancak çakışan içerikler için farklı sonuçlar verebilir. İçeriğin çakışmadığı önceden bilinen kullanım alanlarında, bu yöntem CompositingStrategy.Auto
ile 1'den küçük alfa değerlerine kıyasla daha iyi performans sağlayabilir.
Farklı kompozisyon stratejilerine ilişkin başka bir örnek aşağıda verilmiştir. Bu örnekte, composable'ların farklı bölümlerine farklı alfa değerleri ve Modulate
stratejisi uygulanmaktadır:
@Preview @Composable fun CompositingStrategy_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)

Bir composable'ın içeriğini bit eşleme olarak yazma
Yaygın bir kullanım alanı, bir composable'dan Bitmap
oluşturmaktır. Composable'ınızın içeriğini Bitmap
kopyalamak için rememberGraphicsLayer()
kullanarak GraphicsLayer
oluşturun.
drawWithContent()
ve graphicsLayer.record{}
kullanarak çizim komutlarını yeni katmana yönlendirin. Ardından, drawLayer
kullanarak katmanı görünür tuvalde çizin:
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) }
Bit eşlemi diske kaydedip paylaşabilirsiniz. Daha fazla bilgi için tam örnek snippet'e bakın. Diske kaydetmeyi denemeden önce cihazdaki izinleri kontrol edin.
Özel çizim değiştiricisi
Kendi özel değiştiricinizi oluşturmak için DrawModifier
arayüzünü uygulayın. Bu sayede, ContentDrawScope
kullanırken gösterilenle aynı olan Modifier.drawWithContent()
erişebilirsiniz. Ardından, kodu temizlemek ve uygun sarmalayıcılar sağlamak için ortak çizim işlemlerini özel çizim değiştiricilere ayırabilirsiniz. Örneğin, Modifier.background()
uygun bir DrawModifier
'dir.
Örneğin, içeriği dikey olarak çeviren bir Modifier
uygulamak istiyorsanız bunu aşağıdaki gibi oluşturabilirsiniz:
class FlippedModifier : DrawModifier { override fun ContentDrawScope.draw() { scale(1f, -1f) { this@draw.drawContent() } } } fun Modifier.flipped() = this.then(FlippedModifier())
Ardından, Text
üzerinde uygulanan bu ters çevrilmiş değiştiriciyi kullanın:
Text( "Hello Compose!", modifier = Modifier .flipped() )

Ek kaynaklar
graphicsLayer
ve özel çizim kullanılan daha fazla örnek için aşağıdaki kaynaklara göz atın:
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Oluşturma'daki grafikler
- Resmi özelleştirme {:#customize-image}
- Jetpack Compose için Kotlin