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

Compose'da Brush, bir öğenin ekranda 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çası.

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, DrawScope içinde bir daire çizmek için yatay gradyan fırçası uygulanabilir:

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 gradyanla ç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 ve bunlara karşılık gelen çıktıların listesi:

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

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

Renklerin gradyanda nasıl görüneceğini özelleştirmek için her birinin 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ı farklı miktarlarda (ör. bir renkten daha az veya daha fazla) 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ığı şekilde, belirtilen ofsette dağıtılır. Sarı, kırmızı ve maviye göre daha azdır.

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 deseni tekrarlama

Her gradyan fırçanın üzerinde TileMode ayarlama seçeneği vardır. Gradyan için bir başlangıç ve bitiş ayarlamadıysanız TileMode fark etmeyebilirsiniz. Bu durumda, varsayılan olarak tüm alanı doldurur. TileMode, yalnızca alanın boyutu fırça boyutundan büyükse gradyanı döşer.

Aşağıdaki kod, endX 50.dp ve boyut 200.dp olarak ayarlandığından 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, yukarıdaki HorizontalGradient örneği için farklı karo modlarının ne yaptığı ayrıntılı olarak açıklanmaktadır:

TileMode Çıkış
TileMode.Repeated: Kenar, son renkten ilk renge doğru tekrarlanır. TileMode Tekrarlanıyor
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ını en yakın renge boyar. Karo Modu Kelepçesi
TileMode.Decal: Yalnızca sınırların boyutuna kadar oluşturun. TileMode.Decal, orijinal sınırların dışındaki içerikleri örneklemek için şeffaf siyah kullanırken TileMode.Clamp, kenar rengini örnekler. Karo Modu Çıkartması

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 karoyu endX, yukarıda TileMode bölümünde gördüğümüz gibi ayarlayabilirsiniz. DrawScope içindeyseniz alanın boyutunu öğrenmek 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 boyutunun 4'e bölünmesiyle elde edilen değer
Şekil 3: Gölgelendirici boyutu 4'e bölünür

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

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

Boyut değişiklikleri olmadan dairesel renk geçişi ayarlandı
Şekil 4: Boyut değişikliği olmadan ayarlanan dairesel renk geçişi

Dairesel renk geçişi, yarıçap boyutunu maksimum boyuta ayarlayacak şekilde değiştirildiğinde daha iyi bir dairesel renk geçişi 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 renk geçişinde daha büyük yarıçap
Şekil 5: 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 var. Varsayılan olarak Brush, boyut Brush'nin son oluşturulma boyutundan farklıysa veya gölgelendiricinin oluşturulmasında kullanılan bir durum nesnesi değiştiyse Shader'sini dahili olarak yeniden tahsis eder.

Aşağıdaki kod, çizim alanının boyutu değiştikçe gölgelendiriciyi farklı boyutlarda üç kez 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)
                }
            }
        }
)

Resimleri 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 komutun çıktısı aşağıdaki gibidir:

Farklı şekillerde kullanılan ImageShader fırçası
Şekil 6: Arka plan, metin 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ğuna dikkat edin.

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

AGSL RuntimeShader fırçası

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

Shader fırçası oluşturmak için önce Shader'ı AGSL gölgelendirici 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 yapar. Bu işlem, gradyan efekti oluşturur.

Ardından, Shader Brush'ı oluşturun ve resolution için uniform'ları ayarlayın. Bu, çizim alanının boyutudur. Ayrıca özel gradyanınız için giriş olarak kullanmak istediğiniz color ve color2 değerlerini de ayarlayın:

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ğıdaki sonuçları görürsünüz:

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

Gölgeleyicilerle gradyanlardan çok daha fazlasını yapabilirsiniz. Çünkü her şey matematik tabanlı hesaplamalara dayanır. AGSL hakkında daha fazla bilgi için AGSL belgelerine göz atın.

Ek kaynaklar

Compose'da Brush kullanımıyla ilgili daha fazla örnek için aşağıdaki kaynaklara göz atın: