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

Compose'daki Brush simgesi ekranda bir şeyin nasıl çizildiğini açıklar: Bu simge, çizim alanında çizilen renkleri (ör. daire, kare, yol) belirler. LinearGradient, RadialGradient veya düz bir SolidColor fırça gibi, çizimde kullanışlı birkaç yerleşik Fırça vardır.

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

Örneğin, DrawScope konumunda daire çizmek için yatay bir gradyan fırçası uygulanabilir:

val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue))
Canvas(
    modifier = Modifier.size(200.dp),
    onDraw = {
        drawCircle(brush)
    }
)

Yatay Gradyan ile çizilmiş daire
Şekil 1: Yatay Gradyan ile çizilmiş daire

Gradyan fırçaları

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

Kullanılabilen gradyan fırçalarının listesi ve karşılık gelen çıkışları:

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 yumuşak bir geçiş için son rengi başlangıç rengine ayarlayın.
Süpürme Gradyanı
Brush.radialGradient(colorList) Radyal Gradyan

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

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

Renk duraklarını, daha az veya daha fazla renk 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. Bu şekilde, sarı ve maviden daha az sarı renkte olur.

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

Deseni TileMode ile tekrarla

Her gradyan fırçasının üzerinde bir TileMode ayarlama seçeneği vardır. Renk geçişinin başlangıç ve bitişini varsayılan olarak tüm alanı dolduracak şekilde ayarlamadıysanız TileMode işaretini fark etmeyebilirsiniz. TileMode, yalnızca alanın boyutu Fırça boyutundan büyükse gradyanı döşer.

endX, 50.dp olarak, boyut da 200.dp olarak ayarlandığından aşağıdaki kod, gradyan kalıbını 4 kez tekrarlayacaktır:

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
            )
        )
)

Yukarıdaki HorizontalGradient örneği için farklı karo modlarının işleviyle ilgili ayrıntıların yer aldığı bir tablo aşağıda verilmiştir:

Döşeme Modu Çıkış
TileMode.Repeated: Kenar, son renkten başa doğru tekrarlanır. Parça Modu Tekrarlandı
TileMode.Mirror: Kenar, son renkten başa doğru yansıtılır. Fayans Modu Aynası
TileMode.Clamp: Kenar, son renge sabitlenmiş. Ardından bölgenin geri kalanına en yakın renge boyanır. Karo Modu Sıkıştırma
TileMode.Decal: Yalnızca sınırların boyutuna kadar oluşturun. TileMode.Decal, içeriği orijinal sınırların dışında örneklemek için şeffaf siyahtan yararlanırken, TileMode.Clamp kenar renginden örnekler alır. Karo Modu Çıkartması

TileMode, diğer yönlü renk geçişleriyle benzer şekilde çalışır. Aradaki fark, tekrarın gerçekleştiği yöndür.

Fırça Boyutunu Değiştir

Fırçanızın çizileceği alanın boyutunu biliyorsanız yukarıdaki TileMode bölümünde gördüğümüz şekilde endX blokunu 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, Metin'e atanmışsa) Shader öğesini genişletebilir ve createShader işlevinde çizim alanının boyutundan yararlanabilirsiniz.

Bu örnekte, kalıbı 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ölgelendirici boyutunun 4'e bölümü
Şekil 3: Gölgelendirici boyutunun 4'e bölümü

Ayrıca, 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 alanının 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ünmesiyle sonuçlanır:

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

Boyut değişikliği olmadan Dairesel Renk Geçişi ayarı
Şekil 4: Boyut değişiklikleri olmadan Dairesel Gradyan ayarı

Yarıçap boyutunu maksimum boyuta ayarlamak için dairesel renk geçişi değiştirildiğinde, bu renk geçişinin 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ın boyutuna bağlı olarak dairesel gradyanda daha büyük yarıçap
Şekil 5: Dairesel gradyan üzerinde daha büyük yarıçap (Alanın boyutuna bağlı olarak)

Gölgelendiricinin oluşturulmasına iletilen gerçek boyutun, çağrıldığı yerden belirlendiğini belirtmekte fayda var. Varsayılan olarak boyut, Brush öğesinin son oluşturulma zamanından farklıysa veya gölgelendirici oluşturulurken kullanılan bir durum nesnesi değişirse Brush, Shader değerini dahili olarak yeniden ayırır.

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

Bir 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, arka plan, Metin ve Tuval gibi birkaç farklı çizim türüne uygulanır. Bu işlem şu sonucu verir:

Farklı şekillerde kullanılan ImageShader Fırçası
Şekil 6: Arka plan çizmek, Metin çizmek ve Daire çizmek için ImageShader Fırçası'nı kullanma

Metnin artık metin için pikselleri 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 ile yazılabilir ve Compose'da fırçayla kullanılabilir.

Gölgelendirici fırçası oluşturmak için önce Gölgelendirici'yi 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)) mesafeyi hesaplar ve mesafeye bağlı olarak iki renk arasında bir mix yapar. Bu şekilde bir gradyan efekti oluşturulur.

Ardından, Gölgelendirici Fırçasını oluşturun ve resolution için üniformaları (çizim alanının boyutu) ve özel renk geçişinizde giriş olarak kullanmak istediğiniz color ve color2'yi 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 komut çalıştırıldığında ekranda şunun oluşturulduğunu görebilirsiniz:

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

Hepsi matematik tabanlı hesaplamalar olduğundan, gölgelendiricilerle, renk geçişlerinden çok daha fazlasını yapabileceğinizi belirtmekte fayda var. AGSL hakkında daha fazla bilgi için AGSL belgelerine göz atın.

Ek kaynaklar

Oluşturma özelliğinde Fırça kullanımıyla ilgili daha fazla örnek için aşağıdaki kaynaklara göz atın: