Grafik değiştiriciler

Canvas bileşenine ek olarak Compose, özel içerik çizmeye yardımcı olan çeşitli kullanışlı grafik Modifiers içerir. Bu değiştiriciler, her composable'a uygulanabildiği için kullanışlıdır.

Çizim değiştiriciler

Tüm çizim komutları, Compose'da bir çizim değiştiriciyle yapılır. Compose'da üç ana çizim değiştirici vardır:

Çizim için temel değiştirici drawWithContent'dir. Bu değiştiriciyle, Composable'ınızın çizim sırasını ve değiştirici içinde verilen çizim komutlarını belirleyebilirsiniz. drawBehind, çizim sırası composable'ın içeriğinin arkasına ayarlanmış olan drawWithContent için kullanışlı bir sarmalayıcıdır. drawWithCache, onDrawBehind veya onDrawWithContent işlevini çağırır ve bunlarda oluşturulan nesneleri önbelleğe almak için bir mekanizma sağlar.

Modifier.drawWithContent: Çizim sırasını seçin

Modifier.drawWithContent, composable'ın içeriğinden önce veya sonra DrawScope işlemlerini yürütmenize olanak tanır. Ardından, composable'ın gerçek içeriğini oluşturmak için drawContent işlevini çağırın. Bu değiştiriciyle, içeriğinizin özel çizim işlemlerinizden önce mi yoksa sonra mı çizilmesini istediğinize bağlı olarak işlem sırasını belirleyebilirsiniz.

Örneğin, kullanıcı arayüzünde el feneri anahtar deliği efekti oluşturmak için içeriğinizin üzerine dairesel renk geçişi uygulamak 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
}

Şekil 1: El feneri türünde bir kullanıcı arayüzü deneyimi oluşturmak için bir Composable'ın üzerinde kullanılan Modifier.drawWithContent.

Modifier.drawBehind: Bir composable'ın arkasına çizim yapma

Modifier.drawBehind, ekranda çizilen composable içeriğin arkasında DrawScope işlemleri yapmanıza olanak tanır. Canvas uygulamasını incelediğinizde, bunun Modifier.drawBehind için kullanışlı bir sarmalayıcı olduğunu fark edebilirsiniz.

Text öğesinin arkasına köşeleri 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 da aşağıdaki sonucu verir:

Modifier.drawBehind kullanılarak çizilen metin ve arka plan
Şekil 2: Modifier.drawBehind kullanılarak çizilen metin ve arka plan

Modifier.drawWithCache: Çizim ve önbelleğe alma çizim nesneleri

Modifier.drawWithCache, içinde oluşturulan nesneleri önbelleğe alır. Nesneler, çizim alanının boyutu aynı olduğu veya okunan durum nesneleri değişmediği sürece önbelleğe alınır. Bu değiştirici, çizim sırasında oluşturulan nesnelerin (ör. Brush, Shader, Path vb.) yeniden tahsis edilmesi ihtiyacını ortadan kaldırdığı için çizim çağrılarının performansını artırmak için kullanışlıdır.

Alternatif olarak, nesneleri değiştiricinin dışında remember kullanarak da önbelleğe alabilirsiniz. Ancak, besteye her zaman erişiminiz olmadığı için bu her zaman mümkün olmayabilir. Nesneler yalnızca çizim için kullanılıyorsa drawWithCache kullanmak daha iyi performans sağlayabilir.

Örneğin, bir Text öğesinin arkasına gradyan çizmek için Brush oluşturursanız drawWithCache kullanıldığında çizim alanının boyutu değişene kadar Brush nesnesi önbelleğe alınır:

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

drawWithCache kullanarak Brush nesnesini önbelleğe alma
Şekil 3: drawWithCache ile Brush nesnesini önbelleğe alma

Grafik değiştiriciler

Modifier.graphicsLayer: Oluşturulabilir öğelere dönüşüm işlemleri uygulama

Modifier.graphicsLayer, composable'ın içeriğinin bir çizim katmanına çizilmesini sağlayan bir değiştiricidir. Katmanlar, aşağıdaki gibi birkaç farklı işlev sunar:

  • Çizim talimatları için izolasyon (RenderNode ile benzer). Bir katmanın parçası olarak yakalanan çizim talimatları, uygulama kodunu yeniden yürütmeden oluşturma hattı tarafından verimli bir şekilde yeniden yayınlanabilir.
  • Bir katmanda bulunan tüm çizim talimatları için geçerli olan dönüşümler.
  • Oluşturma özellikleri için pikselleştirme. Bir katman rasterleştirildiğinde çizim talimatları yürütülür ve çıkış, ekran dışı bir arabelleğe alınır. Sonraki kareler için böyle bir arabelleği birleştirmek, bağımsız talimatları yürütmekten daha hızlıdır ancak ölçeklendirme veya döndürme gibi dönüşümler uygulandığında bitmap gibi 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 canlandırılabilir veya değiştirilebilir.

Modifier.graphicsLayer, yalnızca çizim aşamasını etkilediğinden, composable öğenizin ölçülen boyutunu veya yerleşimini değiştirmez. Bu, composable'ınızın düzen sınırlarının dışına çizilmesi durumunda diğerleriyle çakışabileceği anlamına gelir.

Bu değiştiriciyle aşağıdaki dönüşümler uygulanabilir:

Ölçeklendirme - boyutu artırma

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ı gösterir. 0.5f değeri, boyutun yarısı anlamına gelir.

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

Şekil 4: Bir Image composable'a uygulanan scaleX ve scaleY
Çeviri

translationX ve translationY, graphicsLayer ile değiştirilebilir. translationX, composable'ı sola veya sağa taşır. translationY, birleştirilebilir öğeyi 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()
        }
)

Şekil 5: Modifier.graphicsLayer ile görsele uygulanan translationX ve translationY
Döndürme

Yatay döndürme için rotationX, dikey döndürme için rotationY ve Z ekseninde döndürme (standart döndürme) için rotationZ değerini ayarlayın. Bu değer derece (0-360) cinsinden belirtilir.

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

Şekil 6: Modifier.graphicsLayer tarafından Image'da ayarlanan rotationX, rotationY ve rotationZ
Kaynak

transformOrigin belirtilebilir. Ardından bu nokta, dönüşümlerin gerçekleştiği nokta olarak kullanılır. Şimdiye kadarki tüm örneklerde (0.5f, 0.5f) adresindeki TransformOrigin.Center kullanıldı. Başlangıç noktasını (0f, 0f) olarak belirtirseniz dönüşümler, composable'ın sol üst köşesinden başlar.

Başlangıç noktasını rotationZ dönüşümüyle değiştirirseniz öğenin, composable'ın sol üst kısmı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
        }
)

Şekil 7: TransformOrigin 0f, 0f olarak ayarlanmış şekilde uygulanan döndürme

Klip ve Şekil

Şekil, clip = true olduğunda içeriğin kırpılacağı ana hattı belirtir. Bu örnekte, iki kutuyu iki farklı klip içerecek şekilde ayarladık. Bunlardan biri graphicsLayer klip değişkenini, diğeri ise kullanışlı sarmalayıcı Modifier.clip'yı 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 ("Hello Compose" metni) daire şekline kırpılır:

Klip, Box composable'a uygulandı
Şekil 8: Box composable'a uygulanan klip

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

Klip, Y ekseninde çeviri ve dış hat için kırmızı kenarlıkla uygulanır.
Şekil 9: translationY ile uygulanan klip ve ana hat için kırmızı kenarlık

Oluşturulabilir öğeyi çizildiği bölgeye kırpmak için değiştirici zincirinin başına başka bir Modifier.clip(RectangleShape) ekleyebilirsiniz. Böylece içerik, orijinal sınırların 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))
    )
}

graphicsLayer dönüşümünün üzerine uygulanan klip
Şekil 10: graphicsLayer dönüşümünün üzerine uygulanan kırpma

Alfa

Modifier.graphicsLayer, katmanın tamamı için alpha (opaklık) ayarlamak amacıyla 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
        }
)

Alfa uygulanmış resim
Şekil 11: Alfa uygulanmış resim

Birleştirme stratejisi

Alfa ve şeffaflıkla çalışmak her zaman tek bir alfa değerini değiştirmek kadar basit değildir. Alfa değerini değiştirmenin yanı sıra graphicsLayer için CompositingStrategy ayarlama seçeneği de vardır. CompositingStrategy, composable'ın içeriğinin ekranda ç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, diğer graphicsLayer parametreleri tarafından belirlenir. Alfa 1.0f'den küçükse veya bir RenderEffect ayarlanmışsa katmanı ekran dışı bir arabelleğe oluşturur. Alfa değeri 1f'den küçük olduğunda, içerikleri oluşturmak için otomatik olarak bir birleştirme katmanı oluşturulur ve ardından bu ekran dışı arabellek, ilgili alfa ile hedefe çizilir. RenderEffect veya aşırı kaydırma ayarlandığında, ayarlanan CompositingStrategy değerinden bağımsız olarak içerik her zaman ekran dışı bir arabelleğe aktarılır.

Ekran dışında

Birleştirilebilir öğenin içeriği, hedefe oluşturulmadan önce her zaman ekran dışı bir dokuya veya bit eşleme olarak rasterleştirilir. Bu, maske içeriğine BlendMode işlemleri uygulamak ve karmaşık çizim talimatı gruplarını oluştururken performans açısından kullanışlıdır.

CompositingStrategy.Offscreen, BlendModes ile birlikte kullanılabilir. Aşağıdaki örnekte, BlendMode.Clear kullanan bir çizim komutu vererek Image composable'ının parçalarını kaldırmak istediğinizi varsayalım. compositingStrategy özelliğini CompositingStrategy.Offscreen olarak ayarlamazsanız BlendMode, tüm arka plan içeriğiyle etkileşim kurar.

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 olarak ayarlandığında komutların yürütüleceği ekran dışı bir doku oluşturulur (BlendMode yalnızca bu composable'ın içeriğine uygulanır). Ardından, ekranda oluşturulmuş olan içeriğin üzerine oluşturur ve önceden çizilmiş içeriği etkilemez.

Uygulama içinde BlendMode.Clear ile daire göstergesi gösteren bir resimde Modifier.drawWithContent
Şekil 12: Uygulama içinde BlendMode.Clear ve CompositingStrategy.Offscreen ile daire göstergesi gösteren bir resimde Modifier.drawWithContent

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

BlendMode.Clear ve CompositingStrategy ayarlanmadan daire göstergesi gösteren bir resimde Modifier.drawWithContent
Şekil 13: BlendMode.Clear ile ve CompositingStrategy ayarlanmadan daire göstergesi gösteren bir resimde Modifier.drawWithContent

Bu konuyu biraz daha ayrıntılı inceleyelim: Uygulamanın yarı saydam bir pencere arka planı varsa ve CompositingStrategy.Offscreen kullanmadıysanız BlendMode, uygulamanın tamamıyla etkileşim kurar. Alttaki uygulamayı veya duvar kağıdını göstermek için tüm pikselleri temizler. Bu örnekte olduğu gibi:

CompositingStrategy ayarlanmamış ve yarı saydam pencere arka planına sahip bir uygulamayla BlendMode.Clear kullanılıyor. Pembe duvar kağıdı, kırmızı durum dairesinin etrafındaki alanda gösterilir.
Şekil 14: CompositingStrategy ayarlanmamış ve yarı saydam pencere arka planına sahip bir uygulamada BlendMode.Clear kullanılıyor. Pembe duvar kağıdının, kırmızı durum dairesinin etrafındaki alandan nasıl göründüğüne dikkat edin.

CompositingStrategy.Offscreen kullanılırken çizim alanı boyutunda bir ekran dışı doku oluşturulur ve ekrana geri aktarılır. 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ışı dokuları kullanmaya geçiş yaparken ortaya çıkan farklılıkları göstermektedir:

@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 ve CompositingStrategy.Offscreen karşılaştırması: Offscreen, bölgeye kırpma yaparken Auto yapmaz.
Şekil 15: CompositingStrategy.Auto ile CompositingStrategy.Offscreen karşılaştırması. Offscreen, bölgeye kırpma yaparken Auto yapmaz.
ModulateAlpha

Bu kompozisyon stratejisi, graphicsLayer içinde kaydedilen çizim talimatlarının her biri için alfayı modüle eder. RenderEffect ayarlanmadığı sürece 1, 0f'den küçük 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, 1'den küçük alfa değerleriyle CompositingStrategy.Auto değerinden daha iyi performans sağlayabilir.

Aşağıdaki örnekte, farklı birleştirme stratejileri gösterilmektedir. Birleştirilebilir öğelerin farklı bölümlerine farklı alfalar uygulanır ve Modulate stratejisi kullanılı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)
    )
}

@Preview
@Composable
fun ModifierGraphicsLayerOutsets() {
    Column(
        modifier = Modifier
            .background(Color(0xFF0A0E14))
            .padding(48.dp),
        verticalArrangement = Arrangement.spacedBy(48.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        val pillShape = RoundedCornerShape(percent = 50)

        // Without outsets: alpha = 0.5f creates an offscreen buffer that clips the 24.dp glow
        Box(
            modifier = Modifier
                .size(
                    width = 140.dp,
                    height = 56.dp
                )
                .graphicsLayer(
                    alpha = 0.5f,
                    clip = false
                )
                .dropShadow(
                    shape = pillShape,
                    shadow = Shadow(radius = 24.dp, color = Color.Cyan)
                )
                .background(color = Color(0xFF141824), shape = pillShape),
            contentAlignment = Alignment.Center
        ) {
            Text(
                text = "✨ Glow",
                color = Color.White,
                fontSize = 18.sp
            )
        }

        Box(
            modifier = Modifier
                .size(
                    width = 140.dp,
                    height = 56.dp
                )
                .graphicsLayer(
                    alpha = 0.5f,
                    outsets = LayerOutsets(24.dp),
                    clip = false
                )
                .dropShadow(
                    shape = pillShape,
                    shadow = Shadow(radius = 24.dp, color = Color.Cyan)
                )
                .background(color = Color(0xFF141824), shape = pillShape),
            contentAlignment = Alignment.Center
        ) {
            Text(
                text = "✨ Glow",
                color = Color.White,
                fontSize = 18.sp
            )
        }
    }
}


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

ModulateAlpha, her bir çizim komutuna alfa kümesini uygular.
Şekil 16: ModulateAlpha, alfa grubunu her bir çizim komutuna uygular

Katman çıkıntıları

Bir composable, ekran dışı bir arabelleğe çizildiğinde (örneğin, alpha, 1.0f değerinden küçük olduğunda, bir RenderEffect ayarlandığında veya CompositingStrategy.Offscreen kullanıldığında) ekran dışı doku, varsayılan olarak composable'ın ölçülen düzen sınırlarına ayarlanır. Katmanın içinde, composable'ın düzen sınırlarının dışına taşan tüm çizimler veya gölgeler (ör. Modifier.dropShadow ile çizilen dış ışık), ekran dışı arabelleğin sınırında dolaylı olarak kırpılır.

Modifier.padding() eklemek (bitişik kullanıcı arayüzü öğelerinin düzen ölçümünü değiştirir) yerine Modifier.graphicsLayer öğesinin outsets parametresine LayerOutsets değerini iletin. Bu, birleştirilebilir öğenin ölçülen düzen boyutunu değiştirmeden ekran dışı arabelleğin her iki taraftaki görsel sınırlarını genişletir. LayerOutsets, katmanın klip şeklini veya gölge sınırlarını genişletmez. Bunlar, composable'ın orijinal ölçülen boyutuna göre kalır (bu nedenle, orijinal sınırlara kırpılmayı önlemek için graphicsLayer üzerinde clip = false değerini koruyun):

Box(
    modifier = Modifier
        .size(
            width = 140.dp,
            height = 56.dp
        )
        .graphicsLayer(
            alpha = 0.5f,
            outsets = LayerOutsets(24.dp),
            clip = false
        )
        .dropShadow(
            shape = pillShape,
            shadow = Shadow(radius = 24.dp, color = Color.Cyan)
        )
        .background(color = Color(0xFF141824), shape = pillShape),
    contentAlignment = Alignment.Center
) {
    Text(
        text = "✨ Glow",
        color = Color.White,
        fontSize = 18.sp
    )
}

LayerOutsets içermeyen (üstte, dikdörtgen düzen sınırlarına göre kırpılmış) ve LayerOutsets(24.dp) içeren (altta, kırpılmamış) ekran dışı katman dış ışımasının karşılaştırması
Şekil 17: LayerOutsets olmadan (üstte, composable'ın dikdörtgen düzen sınırlarında kırpılmış) ve LayerOutsets(24.dp) ile (altta, kırpılmamış) ekran dışı katman dış parıltısı

Bir composable'ın içeriğini bit eşleme dosyasına yazma

Yaygın kullanım alanlarından biri, composable'dan Bitmap oluşturmaktır. Birleştirilebilir öğenizin içeriğini bir Bitmap öğesine kopyalamak için rememberGraphicsLayer() kullanarak bir GraphicsLayer oluşturun.

drawWithContent() ve graphicsLayer.record{} kullanarak çizim komutlarını yeni katmana yönlendirin. Ardından, görünür tuvalde katmanı çizmek için şunları kullanın 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)
}

Bit eşlemi diske kaydedip paylaşabilirsiniz. Daha fazla bilgi için tam örnek snippet'e bakın. Diske kaydetmeye çalışmadan önce cihaz izinlerini kontrol ettiğinizden emin olun.

Özel çizim değiştirici

Kendi özel değiştiricinizi oluşturmak için DrawModifier arayüzünü uygulayın. Bu sayede, Modifier.drawWithContent() kullanırken gösterilenle aynı olan bir ContentDrawScope öğesine erişebilirsiniz. Ardından, kodu temizlemek ve kullanışlı sarmalayıcılar sağlamak için yaygın çizim işlemlerini özel çizim değiştiricilere ayırabilirsiniz. Örneğin, Modifier.background() kullanışlı bir DrawModifier'dir.

Örneğin, içeriği dikey olarak çeviren bir Modifier uygulamak istiyorsanız aşağıdaki gibi bir oluşturabilirsiniz:

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

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

Ardından, Text için uygulanan bu ters çevrilmiş değiştiriciyi kullanın:

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

Metin Üzerinde Özel Çevrilmiş Değiştirici
Şekil 17: Metin üzerinde özel ters çevrilmiş değiştirici

Ek kaynaklar

graphicsLayer ve özel çizim kullanan daha fazla örnek için aşağıdaki kaynaklara göz atın: