Oluşturulan Grafikler

Birçok uygulamanın, ekranda çizilenleri tam olarak kontrol edebilmesi gerekir. Bu, ekrana tam doğru yere bir kutu veya daire yerleştirmek kadar basit ya da birçok farklı stildeki grafik öğelerinin ayrıntılı bir düzenlemesi 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ştiriciler kullanmaktır.

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

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

Tek ihtiyacınız olan çizim yapan bir composable ise Canvas composable'ı kullanabilirsiniz. Canvas composable'ı, Modifier.drawBehind için kullanışlı bir sarmalayıcıdır. Canvas öğesini, diğer Compose kullanıcı arayüzü öğelerinde olduğu gibi düzeninize yerleştirirsiniz. Canvas içinde, öğeleri stilleri ve konumları üzerinde hassas kontrolle çizebilirsiniz.

Tüm çizim değiştiriciler, kendi durumunu koruyan kapsamlı bir çizim ortamı olan DrawScope'ı kullanır. Bu sayede, bir grup grafik öğesi için parametreleri ayarlayabilirsiniz. DrawScope, size gibi çeşitli yararlı alanlar sağlar. DrawScope'nın mevcut boyutlarını belirten bir Size nesnesi de bu alanlar arasındadır.

Bir şeyler çizmek için DrawScope üzerindeki birçok çizim işlevinden birini kullanabilirsiniz. Örneğin, aşağıdaki kod ekranın sol üst köşesinde 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 dikdörtgen
1. Şekil. Oluşturma penceresinde Tuval kullanılarak çizilmiş 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 birçok çizim yönteminde konum ve boyut, varsayılan parametre değerleri tarafından sağlanır. Varsayılan parametreler genellikle öğeyi tuval üzerinde [0, 0] noktasına yerleştirir ve yukarıdaki örnekte olduğu gibi çizim alanının tamamı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 Oluşturma'daki koordinat sistemini anlamanız gerekir.

Koordinat sisteminin başlangıç noktası ([0,0]), çizim alanındaki en soldaki üst pikseldir. x sağa doğru hareket ettirildikçe, y ise aşağı doğru hareket ettirildikçe artar.

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

Örneğin, tuval alanının sağ üst köşesinden sol alt köşesine doğru çapraz bir çizgi çizmek istiyorsanız DrawScope.drawLine() işlevini kullanabilir ve başlangıç ile bitiş uzaklığını ilgili x ve y konumlarıyla 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üştürmeler sunar.

Ölçek

Çizim işlemlerinizin boyutunu belirli bir faktör kadar artırmak için DrawScope.scale() kullanın. Aşağıdaki gibi işlemler scale() ilgili lambda içindeki tüm çizim işlemleri için geçerlidir. Ö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())
    }
}

Tek tip olmayan şekilde ölçeklendirilmiş bir daire
3. Şekil. Canvas'ta bir daireye ölçek işlemi uygulama.

Çeviri

Çizim işlemlerinizi yukarı, aşağı, sola veya sağa taşımak için DrawScope.translate() simgesini 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 uzaklaşmış bir daire
4. Şekil Canvas'taki bir daireye çeviri işlemi uygulama.

Döndür

Çizim işlemlerinizi bir pivot noktası etrafında döndürmek için DrawScope.rotate() simgesini 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 Dikdörtgeni 45 derece döndüren mevcut çizim kapsamına döndürme uygulamak için rotate() kullanırız.

İçe doğru

Çizim sınırlarını değiştirmek ve çizimleri buna göre çevirmek için DrawScope.inset() simgesini kullanarak mevcut DrawScope öğesinin varsayılan parametrelerini ayarlayın:

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ında dolgu bulunan bir dikdörtgen
6. Şekil. Çizim komutlarına iç kenar 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şturup uygular. Tüm dönüşümler tek bir işlemde birlikte gerçekleştirildiğinden, iç içe yerleştirilmiş dönüşümlerin her birinin Compose tarafından hesaplanıp kaydedilmesi gerekmez. Bu nedenle, withTransform() kullanmak, bağımsız dönüşümlere iç içe yerleştirilmiş çağrılar yapmaktan daha verimlidir.

Ö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 kenarına kaydırılmış, döndürülmüş dikdörtgenli bir telefon
7. Şekil. Hem döndürme hem de öteleme uygulamak için withTransform'yı kullanın. Bu işlemde dikdörtgen döndürülür ve sola kaydırılır.

Sık kullanılan çizim işlemleri

Çizim metni

Oluşturma'da metin çizmek için genellikle Text composable'ı 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 TextMeasurer oluşturun ve ölçüm cihazıyla drawText'ı çağırın:

val textMeasurer = rememberTextMeasurer()

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

Tuval'de çizilmiş bir "Merhaba" gösteriliyor.
Şekil 8. Tuval üzerine metin çizme.

Metni ölçme

Çizim metni, diğer çizim komutlarından biraz farklı çalışır. Normalde, çizim komutuna şekli/resmi çizeceği boyutu (genişlik ve yükseklik) verirsiniz. Metinle ilgili olarak, oluşturulan metnin boyutunu kontrol eden birkaç parametre vardır. Bunlar arasında yazı tipi boyutu, yazı tipi, bağlaçlar ve harf aralığı yer alır.

Oluşturma özelliğiyle, yukarıdaki faktörlere bağlı olarak metnin ölçülen boyutuna erişmek için TextMeasurer kullanabilirsiniz. Metnin arkasına bir 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:

Tam alanın 2/3'ünü kaplayan, arka planı dikdörtgen olan çok satırlı metin
Şekil 9. Tam alanın ⅔'ünü kaplayan, arka planı dikdörtgen olan çok satırlı metin.

Kısıtlamaların, yazı tipi boyutunun veya ölçülen boyutu etkileyen herhangi bir özelliğin ayarlanması, yeni bir boyutun raporlanmasına neden olur. Hem width hem de height için sabit bir boyut belirleyebilirsiniz. Metin, belirlenen TextOverflow değerine göre ayarlanır. Örneğin, aşağıdaki kod, metni composable alanın yüksekliğinin ⅓'ü ve genişliğinin ⅓'ü kadar olacak şekilde 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 sınırlamalar içinde çiziliyor ve sonunda üç nokta bulunuyor:

Pembe arka plan üzerinde çizilmiş, üç nokta ile kesilmiş metin.
10. Şekil. TextOverflow.Ellipsis metin ölçümüyle ilgili sabit kısıtlamalarla.

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 çizme.

Temel şekiller çizme

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

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

API

Çıkış

drawCircle()

daire çizme

drawRect()

draw rect

drawRoundedRect()

yuvarlatılmış dikdörtgen çiz

drawLine()

çizgi çizme

drawOval()

oval çiz

drawArc()

yay çiz

drawPoints()

çizim noktaları

Çizim yolu

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

Örneğin, üçgen çizmek istediğinizi varsayalım. Çizim alanının boyutunu kullanarak lineTo() ve moveTo() gibi işlevlerle 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()
)

Oluştur'da çizilmiş, ters dönmüş mor renkli yol üçgeni
Şekil 12. Oluşturma penceresinde Path oluşturma ve çizme.

Canvas nesnesine erişme

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

Örneğin, tuvale çizmek istediğiniz özel bir Drawable varsa tuvale erişebilir ve Drawable#draw() işlevini çağırarak 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 boyutta oval siyah bir ShapeDrawable
Şekil 13. Drawable çizmek için tuvale erişme.

Daha fazla bilgi

Oluşturma modunda çizim yapma hakkında daha fazla bilgi için aşağıdaki kaynaklara göz atın: