Oluşturulan Grafikler

Birçok uygulamanın, ekranda tam olarak neyin çizildiğini hassas bir şekilde kontrol edebilmesi gerekir. Bu, ekrana doğru yere bir kutu veya daire yerleştirmek kadar küçük bir şey olabileceği gibi, birçok farklı stilde grafik öğelerinin ayrıntılı bir düzenlemesi de olabilir.

Değiştiriciler ve DrawScope ile temel çizim

Compose'da özel bir şey çizmenin temel yolu, Modifier.drawWithContent, Modifier.drawBehind ve Modifier.drawWithCache gibi değiştiricileri kullanmaktır.

Örneğin, composable'ınızın arkasına bir şey çizmek için drawBehind değiştiricisini kullanarak çizim komutlarını yürütmeye başlayabilirsiniz:

Spacer(
    modifier = Modifier
        .fillMaxSize()
        .drawBehind {
            // this = DrawScope
        }
)

Yalnızca çizim yapan bir composable'a ihtiyacınız varsa Canvas composable'ını kullanabilirsiniz. Canvas composable'ı, Modifier.drawBehind için kullanışlı bir sarmalayıcıdır. Canvas öğesini, diğer tüm Compose kullanıcı arayüzü öğelerinde olduğu gibi düzeninize yerleştirirsiniz. Canvas içinde, stil ve konumları üzerinde hassas kontrol sahibi olarak öğeler çizebilirsiniz.

Tüm çizim değiştiriciler, kendi durumunu koruyan kapsamlı bir çizim ortamı olan DrawScope öğesini gösterir. Bu sayede, bir grup grafik öğesinin parametrelerini ayarlayabilirsiniz. DrawScope, size gibi çeşitli faydalı alanlar sağlar. Bu alan, DrawScope öğesinin mevcut boyutlarını belirten bir Size nesnesidir.

Çizim yapmak için DrawScope üzerindeki birçok çizim işlevinden birini kullanabilirsiniz. Örneğin, aşağıdaki kod ekranın sol üst köşesine bir dikdörtgen çizer:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    drawRect(
        color = Color.Magenta,
        size = canvasQuadrantSize
    )
}

Beyaz arka plan üzerinde ekranın dörtte birini kaplayan pembe bir dikdörtgen çizimi
Şekil 1. Compose'da Canvas kullanılarak çizilen dikdörtgen.

Farklı çizim değiştiriciler hakkında daha fazla bilgi edinmek için Grafik Değiştiriciler belgelerine bakın.

Koordinat sistemi

Ekranda bir şey çizmek için öğenizin ofsetini (x ve y) ve boyutunu bilmeniz gerekir. DrawScope üzerindeki çizim yöntemlerinin çoğunda konum ve boyut, varsayılan parametre değerleri tarafından sağlanır. Varsayılan parametreler genellikle öğeyi tuvalde [0, 0] noktasına yerleştirir ve yukarıdaki örnekte olduğu gibi tüm çizim alanını dolduran varsayılan bir size sağlar. Dikdörtgenin sol üstte konumlandırıldığını görebilirsiniz. Öğenizin boyutunu ve konumunu ayarlamak için Compose'daki koordinat sistemini anlamanız gerekir.

Koordinat sisteminin başlangıç noktası ([0,0]), çizim alanının en sol üst pikselindedir. x sağa doğru hareket ettikçe artar, y ise aşağı doğru hareket ettikçe artar.

Sol üst [0, 0] ve sağ alt [genişlik, yükseklik] koordinat sistemini gösteren bir ızgara
Şekil 2. Çizim koordinat sistemi / çizim kılavuzu.

Örneğin, tuval alanının sağ üst köşesinden sol alt köşesine çapraz bir çizgi çizmek istiyorsanız DrawScope.drawLine() işlevini kullanabilir ve ilgili x ve y konumlarıyla bir başlangıç ve bitiş ofseti belirtebilirsiniz:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height
    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
}

Temel dönüşümler

DrawScope, çizim komutlarının nerede veya nasıl yürütüleceğini değiştirmek için dönüşümler sunar.

Ölçek

Çizim işlemlerinizin boyutunu bir katsayı kadar artırmak için DrawScope.scale() kullanın. scale() gibi işlemler, ilgili lambda içindeki tüm çizim işlemlerine uygulanır. Örneğin, aşağıdaki kod scaleX değerini 10 kat, scaleY değerini ise 15 kat artırır:

Canvas(modifier = Modifier.fillMaxSize()) {
    scale(scaleX = 10f, scaleY = 15f) {
        drawCircle(Color.Blue, radius = 20.dp.toPx())
    }
}

Düzensiz ölçeklendirilmiş bir daire
Şekil 3. Canvas'ta bir daireye ölçeklendirme işlemi uygulanıyor.

Çevir

Çizim işlemlerinizi yukarı, aşağı, sola veya sağa taşımak için DrawScope.translate() tuşlarını kullanın. Örneğin, aşağıdaki kod çizimi 100 piksel sağa ve 300 piksel yukarı taşır:

Canvas(modifier = Modifier.fillMaxSize()) {
    translate(left = 100f, top = -300f) {
        drawCircle(Color.Blue, radius = 200.dp.toPx())
    }
}

Merkezden kaymış bir daire
Şekil 4. Canvas'ta bir daireye çeviri işlemi uygulanıyor.

Döndür

Çizim işlemlerinizi bir pivot nokta etrafında döndürmek için DrawScope.rotate() kullanın. Örneğin, aşağıdaki kod bir dikdörtgeni 45 derece döndürür:

Canvas(modifier = Modifier.fillMaxSize()) {
    rotate(degrees = 45F) {
        drawRect(
            color = Color.Gray,
            topLeft = Offset(x = size.width / 3F, y = size.height / 3F),
            size = size / 3F
        )
    }
}

Ekranın ortasında 45 derece döndürülmüş bir dikdörtgen bulunan telefon
5. şekil. Mevcut çizim kapsamına döndürme uygulamak için rotate() kullanırız. Bu, dikdörtgeni 45 derece döndürür.

İçe doğru

Mevcut DrawScope öğesinin varsayılan parametrelerini ayarlamak için DrawScope.inset() kullanın. Bu işlem, çizim sınırlarını değiştirir ve çizimleri buna göre çevirir:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    inset(horizontal = 50f, vertical = 30f) {
        drawRect(color = Color.Green, size = canvasQuadrantSize)
    }
}

Bu kod, çizim komutlarına etkili bir şekilde dolgu ekler:

Her tarafı dolgulu bir dikdörtgen
Şekil 6. Çizim komutlarına içe yerleştirme uygulama

Birden fazla dönüşüm

Çizimlerinize birden fazla dönüşüm uygulamak için DrawScope.withTransform() işlevini kullanın. Bu işlev, istediğiniz tüm değişiklikleri birleştiren tek bir dönüşüm oluşturur ve uygular. withTransform() kullanmak, ayrı ayrı dönüşümlere iç içe yerleştirilmiş çağrılar yapmaktan daha verimlidir. Bunun nedeni, tüm dönüşümlerin tek bir işlemde birlikte gerçekleştirilmesidir. Bu sayede, Compose'un iç içe yerleştirilmiş dönüşümlerin her birini hesaplaması ve kaydetmesi gerekmez.

Örneğin, aşağıdaki kod dikdörtgene hem çeviri hem de döndürme uygular:

Canvas(modifier = Modifier.fillMaxSize()) {
    withTransform({
        translate(left = size.width / 5F)
        rotate(degrees = 45F)
    }) {
        drawRect(
            color = Color.Gray,
            topLeft = Offset(x = size.width / 3F, y = size.height / 3F),
            size = size / 3F
        )
    }
}

Ekranın yan tarafına kaydırılmış, döndürülmüş bir dikdörtgenin bulunduğu telefon
Şekil 7. withTransform kullanarak hem döndürme hem de öteleme uygulayın. Dikdörtgeni döndürün ve sola kaydırın.

Sık kullanılan çizim işlemleri

Metin çizme

Compose'da metin çizmek için genellikle Text composable'ını kullanabilirsiniz. Ancak DrawScope içindeyseniz veya metninizi özelleştirerek manuel olarak çizmek istiyorsanız DrawScope.drawText() yöntemini kullanabilirsiniz.

Metin çizmek için rememberTextMeasurer kullanarak bir TextMeasurer oluşturun ve ölçüm aracıyla drawText işlevini çağırın:

val textMeasurer = rememberTextMeasurer()

Canvas(modifier = Modifier.fillMaxSize()) {
    drawText(textMeasurer, "Hello")
}

Canvas'ta çizilmiş bir "Merhaba" yazısı gösteriliyor.
Şekil 8. Canvas'ta metin çizme

Metni ölçme

Metin çizme, diğer çizim komutlarından biraz farklı çalışır. Normalde, çizim komutuna şeklin/resmin çizileceği boyutu (genişlik ve yükseklik) verirsiniz. Metinlerde, oluşturulan metnin boyutunu kontrol eden birkaç parametre vardır. Örneğin, yazı tipi boyutu, yazı tipi, bitişik harfler ve harf aralığı.

Compose ile yukarıdaki faktörlere bağlı olarak metnin ölçülen boyutuna erişmek için TextMeasurer kullanabilirsiniz. Metnin arkasına arka plan çizmek istiyorsanız metnin kapladığı alanın boyutunu öğrenmek için ölçülen bilgileri kullanabilirsiniz:

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()),
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

Bu kod snippet'i, metinde pembe bir arka plan oluşturur:

Arka planda dikdörtgen bulunan, tüm alanın ⅔'ünü kaplayan çok satırlı metin
Şekil 9. Arka plan dikdörtgeniyle birlikte tam alanın ⅔'ünü kaplayan çok satırlı metin.

Kısıtlamaları, yazı tipi boyutunu veya ölçülen boyutu etkileyen herhangi bir özelliği ayarlamak, yeni bir boyutun bildirilmesine neden olur. Hem width hem de height için sabit bir boyut ayarlayabilirsiniz. Metin, ayarlanan TextOverflow değerine uyar. Örneğin, aşağıdaki kod, metni composable alanın yüksekliğinin ⅓'ü ve genişliğinin ⅓'ü kadar oluşturur ve TextOverflow değerini TextOverflow.Ellipsis olarak ayarlar:

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixed(
                        width = (size.width / 3f).toInt(),
                        height = (size.height / 3f).toInt()
                    ),
                    overflow = TextOverflow.Ellipsis,
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

Metin artık kısıtlamalarda sonunda üç nokta olacak şekilde çizilir:

Pembe arka plan üzerine yazılmış, üç nokta ile kesilen metin.
10. şekil. TextOverflow.Ellipsis, metin ölçümünde sabit kısıtlamalara sahip.

Resim çizme

DrawScope ile ImageBitmap çizmek için ImageBitmap.imageResource() kullanarak resmi yükleyin ve ardından drawImage işlevini çağırın:

val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)

Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
    drawImage(dogImage)
})

Canvas'ta çizilmiş bir köpek resmi
Şekil 11. Canvas'ta ImageBitmap çiziliyor.

Temel şekiller çizme

DrawScope üzerinde birçok şekil çizme işlevi vardır. Şekil çizmek için önceden tanımlanmış çizim işlevlerinden birini (ör. drawCircle) kullanın:

val purpleColor = Color(0xFFBA68C8)
Canvas(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp),
    onDraw = {
        drawCircle(purpleColor)
    }
)

API

Çıkış

drawCircle()

daire çiz

drawRect()

dikdörtgen çiz

drawRoundedRect()

yuvarlatılmış dikdörtgen çiz

drawLine()

çizgi çiz

drawOval()

oval çiz

drawArc()

yay çiz

drawPoints()

puanları çiz

Yol çizme

Yol, yürütüldüğünde çizimle sonuçlanan bir dizi matematiksel talimattır. DrawScope, DrawScope.drawPath() yöntemini kullanarak yol çizebilir.

Örneğin, bir üçgen çizmek istediğinizi varsayalım. Çizim alanının boyutunu kullanarak lineTo() ve moveTo() gibi işlevlerle bir yol oluşturabilirsiniz. Ardından, üçgen elde etmek için yeni oluşturulan bu yolla drawPath() işlevini çağırın.

Spacer(
    modifier = Modifier
        .drawWithCache {
            val path = Path()
            path.moveTo(0f, 0f)
            path.lineTo(size.width / 2f, size.height / 2f)
            path.lineTo(size.width, 0f)
            path.close()
            onDrawBehind {
                drawPath(path, Color.Magenta, style = Stroke(width = 10f))
            }
        }
        .fillMaxSize()
)

Compose'da çizilmiş ters mor yol üçgeni
Şekil 12. Oluşturma modunda Path oluşturma ve çizme

Canvas nesnesine erişiliyor

DrawScope ile Canvas nesnesine doğrudan erişiminiz yoktur. İşlevleri çağırabileceğiniz Canvas nesnesine erişmek için DrawScope.drawIntoCanvas() kullanabilirsiniz.

Örneğin, tuval üzerine çizmek istediğiniz özel bir Drawable varsa tuvale erişip Drawable#draw() öğesini çağırabilir ve Canvas nesnesini iletebilirsiniz:

val drawable = ShapeDrawable(OvalShape())
Spacer(
    modifier = Modifier
        .drawWithContent {
            drawIntoCanvas { canvas ->
                drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt())
                drawable.draw(canvas.nativeCanvas)
            }
        }
        .fillMaxSize()
)

Tam boyutu kaplayan oval siyah bir ShapeDrawable
Şekil 13. Drawable çizmek için tuvale erişiliyor.

Daha fazla bilgi

Compose'da çizim hakkında daha fazla bilgi için aşağıdaki kaynaklara göz atın: