Fırça: Renk geçişleri ve gölgelendiriciler

Compose'daki Brush, ekranda bir şeyin nasıl çizildiğini açıklar: Çizim alanında (ör. daire, kare, yol) çizilen renkleri belirler. Çizim için kullanışlı olan birkaç yerleşik fırça vardır. Örneğin, LinearGradient, RadialGradient veya düz bir SolidColor fırça.

Fırçalar, çizilen içeriğe boyama stilini uygulamak için Modifier.background(), TextStyle veya DrawScope çizim çağrılarıyla kullanılabilir.

Örneğin, yatay gradyan fırça, DrawScope içinde daire çizmek için kullanılabilir:

val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue))
Canvas(
    modifier = Modifier.size(200.dp),
    onDraw = {
        drawCircle(brush)
    }
)
Yatay gradyanla çizilmiş daire
Şekil 1: Yatay renk geçişiyle çizilmiş daire

Gradyan fırçalar

Farklı gradyan efektleri elde etmek için kullanılabilecek birçok yerleşik gradyan fırçası vardır. Bu fırçalar, gradyan oluşturmak istediğiniz renklerin listesini belirtmenize olanak tanır.

Kullanılabilir gradyan fırçaların listesi ve ilgili çıkış:

Gradyan Fırça Türü Çıkış
Brush.horizontalGradient(colorList) Yatay Gradyan
Brush.linearGradient(colorList) Doğrusal Gradyan
Brush.verticalGradient(colorList) Dikey Gradyan
Brush.sweepGradient(colorList)
Not: Renkler arasında sorunsuz bir geçiş elde etmek için son rengi başlangıç rengi olarak ayarlayın.
Dairesel Gradyan
Brush.radialGradient(colorList) Dairesel renk geçişi

colorStops ile renk dağılımını değiştirme

Renklerin gradyende nasıl görüneceğini özelleştirmek için her bir renk için colorStops değerini değiştirebilirsiniz. colorStops, 0 ile 1 arasında bir kesir olarak belirtilmelidir. 1'den büyük değerler, bu renklerin gradyanın bir parçası olarak oluşturulmamasına neden olur.

Renk duraklarını, bir rengin daha az veya daha çok olması gibi farklı miktarlarda olacak şekilde yapılandırabilirsiniz:

val colorStops = arrayOf(
    0.0f to Color.Yellow,
    0.2f to Color.Red,
    1f to Color.Blue
)
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(Brush.horizontalGradient(colorStops = colorStops))
)

Renkler, colorStop çiftinde tanımlandığı gibi sağlanan ofsette dağıtılır. Kırmızı ve maviye kıyasla daha az sarı renk bulunur.

Farklı renk duraklarıyla yapılandırılmış fırça
Şekil 2: Farklı renk duraklarıyla yapılandırılmış fırça

TileMode ile bir kalıbı tekrarlama

Her gradyan fırçaya TileMode ekleyebilirsiniz. Gradyan için başlangıç ve bitiş belirlemediyseniz TileMode simgesini fark etmeyebilirsiniz. Bu durumda, varsayılan olarak tüm alan doldurulur. TileMode, yalnızca alanın boyutu fırça boyutundan büyükse gradyanı döşer.

endX değeri 50.dp, boyut değeri ise 200.dp olarak ayarlandığından aşağıdaki kod, gradyan desenini 4 kez tekrarlar:

val listColors = listOf(Color.Yellow, Color.Red, Color.Blue)
val tileSize = with(LocalDensity.current) {
    50.dp.toPx()
}
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(
            Brush.horizontalGradient(
                listColors,
                endX = tileSize,
                tileMode = TileMode.Repeated
            )
        )
)

Aşağıdaki tabloda, farklı Döşeme Modlarının yukarıdaki HorizontalGradient örneğinde ne yaptığı ayrıntılı olarak açıklanmaktadır:

TileMode Çıkış
TileMode.Repeated: Kenar, son renkten ilk renge doğru tekrarlanır. TileMode Repeated
TileMode.Mirror: Kenar, son renkten ilk renge doğru yansıtılır. TileMode Mirror
TileMode.Clamp: Kenar, son renge sabitlenir. Ardından, bölgenin geri kalanında en yakın rengi boyar. Tile Modu Kelepçesi
TileMode.Decal: Yalnızca sınırlar boyutunda oluşturun. TileMode.Decal, orijinal sınırların dışındaki içeriği örneklemek için şeffaf siyahı kullanırken TileMode.Clamp, kenar rengini örnekler. Tile Mode Decal

TileMode, diğer yönlü gradyanlar için de benzer şekilde çalışır. Tek fark, tekrarın gerçekleştiği yöndür.

Fırça boyutunu değiştirme

Fırçanızın çizileceği alanın boyutunu biliyorsanız endX bölümünde yukarıda gördüğümüz gibi döşemeyi TileMode olarak ayarlayabilirsiniz. DrawScope içindeyseniz alanın boyutunu almak için size özelliğini kullanabilirsiniz.

Çizim alanınızın boyutunu bilmiyorsanız (örneğin, Brush metne atanmışsa) Shader öğesini genişletebilir ve createShader işlevinde çizim alanının boyutunu kullanabilirsiniz.

Bu örnekte, deseni 4 kez tekrarlamak için boyutu 4'e bölün:

val listColors = listOf(Color.Yellow, Color.Red, Color.Blue)
val customBrush = remember {
    object : ShaderBrush() {
        override fun createShader(size: Size): Shader {
            return LinearGradientShader(
                colors = listColors,
                from = Offset.Zero,
                to = Offset(size.width / 4f, 0f),
                tileMode = TileMode.Mirror
            )
        }
    }
}
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(customBrush)
)

Gölgeleyici boyutu 4'e bölünür.
Şekil 3: Shader boyutu 4'e bölünmüş

Dairesel renk geçişleri gibi diğer renk geçişlerinin fırça boyutunu da değiştirebilirsiniz. Boyut ve merkez belirtmezseniz gradyan, DrawScope öğesinin tüm sınırlarını kaplar ve dairesel gradyanın merkezi, DrawScope sınırlarının merkezine ayarlanır. Bu durumda, radyal gradyanın merkezi, daha küçük boyutun (genişlik veya yükseklik) merkezi olarak görünür:

Box(
    modifier = Modifier
        .fillMaxSize()
        .background(
            Brush.radialGradient(
                listOf(Color(0xFF2be4dc), Color(0xFF243484))
            )
        )
)

Boyut değişiklikleri olmadan ayarlanan radyal gradyan
Şekil 4: Boyut değişiklikleri olmadan ayarlanan dairesel renk geçişi

Radyal gradyan, yarıçap boyutu maksimum boyuta ayarlanacak şekilde değiştirildiğinde daha iyi bir radyal gradyan efekti oluşturduğunu görebilirsiniz:

val largeRadialGradient = object : ShaderBrush() {
    override fun createShader(size: Size): Shader {
        val biggerDimension = maxOf(size.height, size.width)
        return RadialGradientShader(
            colors = listOf(Color(0xFF2be4dc), Color(0xFF243484)),
            center = size.center,
            radius = biggerDimension / 2f,
            colorStops = listOf(0f, 0.95f)
        )
    }
}

Box(
    modifier = Modifier
        .fillMaxSize()
        .background(largeRadialGradient)
)

Alan boyutuna göre dairesel gradyanda daha büyük yarıçap
5. Şekil: Alanın boyutuna göre dairesel renk geçişinde daha büyük yarıçap

Gölgelendiricinin oluşturulmasına aktarılan gerçek boyutun, çağrıldığı yere göre belirlendiğini belirtmekte fayda vardır. Varsayılan olarak, Brush boyutu Brush'nın son oluşturulmasından farklıysa veya gölgelendiricinin oluşturulmasında kullanılan bir durum nesnesi değişmişse Brush, Shader'yi dahili olarak yeniden ayırır.

Aşağıdaki kod, çizim alanının boyutu değiştikçe gölgelendiriciyi üç farklı boyutta oluşturur:

val colorStops = arrayOf(
    0.0f to Color.Yellow,
    0.2f to Color.Red,
    1f to Color.Blue
)
val brush = Brush.horizontalGradient(colorStops = colorStops)
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .drawBehind {
            drawRect(brush = brush) // will allocate a shader to occupy the 200 x 200 dp drawing area
            inset(10f) {
      /* Will allocate a shader to occupy the 180 x 180 dp drawing area as the
       inset scope reduces the drawing  area by 10 pixels on the left, top, right,
      bottom sides */
                drawRect(brush = brush)
                inset(5f) {
        /* will allocate a shader to occupy the 170 x 170 dp drawing area as the
         inset scope reduces the  drawing area by 5 pixels on the left, top,
         right, bottom sides */
                    drawRect(brush = brush)
                }
            }
        }
)

Resmi fırça olarak kullanma

ImageBitmap'i Brush olarak kullanmak için resmi ImageBitmap olarak yükleyin ve ImageShader fırçası oluşturun:

val imageBrush =
    ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog)))

// Use ImageShader Brush with background
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(imageBrush)
)

// Use ImageShader Brush with TextStyle
Text(
    text = "Hello Android!",
    style = TextStyle(
        brush = imageBrush,
        fontWeight = FontWeight.ExtraBold,
        fontSize = 36.sp
    )
)

// Use ImageShader Brush with DrawScope#drawCircle()
Canvas(onDraw = {
    drawCircle(imageBrush)
}, modifier = Modifier.size(200.dp))

Fırça, birkaç farklı çizim türüne uygulanır: arka plan, metin ve tuval. Bu işlem aşağıdaki çıkışı verir:

ImageShader fırçası farklı şekillerde kullanılıyor
Şekil 6: Arka plan çizmek, metin çizmek ve daire çizmek için ImageShader fırçasını kullanma

Metnin artık metin piksellerini boyamak için ImageBitmap kullanılarak da oluşturulduğunu unutmayın.

Gelişmiş örnek: Özel fırça

AGSL RuntimeShader fırçası

AGSL, GLSL Shader özelliklerinin bir alt kümesini sunar. Gölgelendiriciler AGSL'de yazılabilir ve Compose'da fırçayla kullanılabilir.

Shader fırçası oluşturmak için önce Shader'ı AGSL shader dizesi olarak tanımlayın:

@Language("AGSL")
val CUSTOM_SHADER = """
    uniform float2 resolution;
    layout(color) uniform half4 color;
    layout(color) uniform half4 color2;

    half4 main(in float2 fragCoord) {
        float2 uv = fragCoord/resolution.xy;

        float mixValue = distance(uv, vec2(0, 1));
        return mix(color, color2, mixValue);
    }
""".trimIndent()

Yukarıdaki gölgelendirici, iki giriş rengi alır, çizim alanının sol alt kısmından (vec2(0, 1)) olan mesafeyi hesaplar ve mesafeye göre iki renk arasında mix işlemi yapar. Bu işlem, renk geçişi efekti oluşturur.

Ardından, gölgelendirici fırçayı oluşturun ve resolution için tek tip değerleri ayarlayın. Bu değerler, çizim alanının boyutu ile özel gradyanınızda giriş olarak kullanmak istediğiniz color ve color2 değerleridir:

val Coral = Color(0xFFF3A397)
val LightYellow = Color(0xFFF8EE94)

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
@Preview
fun ShaderBrushExample() {
    Box(
        modifier = Modifier
            .drawWithCache {
                val shader = RuntimeShader(CUSTOM_SHADER)
                val shaderBrush = ShaderBrush(shader)
                shader.setFloatUniform("resolution", size.width, size.height)
                onDrawBehind {
                    shader.setColorUniform(
                        "color",
                        android.graphics.Color.valueOf(
                            LightYellow.red, LightYellow.green,
                            LightYellow
                                .blue,
                            LightYellow.alpha
                        )
                    )
                    shader.setColorUniform(
                        "color2",
                        android.graphics.Color.valueOf(
                            Coral.red,
                            Coral.green,
                            Coral.blue,
                            Coral.alpha
                        )
                    )
                    drawRect(shaderBrush)
                }
            }
            .fillMaxWidth()
            .height(200.dp)
    )
}

Bu komutu çalıştırdığınızda ekranda aşağıdakiler oluşturulur:

Compose'da çalışan özel AGSL gölgelendiricisi
Şekil 7: Compose'da çalışan özel AGSL gölgelendiricisi

Tamamen matematik tabanlı hesaplamalar olduğundan, gölgelendiricilerle yalnızca gradyanlardan çok daha fazlasını yapabileceğinizi belirtmek isteriz. AGSL hakkında daha fazla bilgi için AGSL dokümanlarına göz atın.

Ek kaynaklar

Oluşturma modunda Fırça'yı kullanmayla ilgili daha fazla örnek için aşağıdaki kaynaklara göz atın: