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

Compose'daki Brush bir şeyin ekranda nasıl çizildiğini açıklar: Çizim alanında çizilen renkleri (ör. daire, kare, yol) belirler. Çizim için yararlı olabilecek birkaç yerleşik Fırça (LinearGradient, RadialGradient veya sade SolidColor fırça gibi) vardır.

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

Örneğin, DrawScope bölgesinde bir 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 çizilen daire
Şekil 1: Yatay Gradyan ile çizilen 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, renk geçişi oluşturmak istediğiniz renklerin listesini belirtmenize olanak tanır.

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

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ş elde etmek için son rengi başlangıç rengine ayarlayın.
Gradyan Süpür
Brush.radialGradient(colorList) Dairesel Gradyan

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

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

Renk duraklarını farklı miktarlara sahip olacak şekilde yapılandırabilirsiniz (ör. tek renkte daha az veya daha fazla):

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, sağlanan ofsette dağıtılır; kırmızı ve maviden daha az sarıdır.

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

Bir deseni TileMode ile tekrarla

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

endX öğesi 50.dp, 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 ne işe yaradığını ayrıntılı bir şekilde açıklayan bir tablo aşağıda verilmiştir:

Parça Modu Çıkış
TileMode.Repeated: Kenar, son renkten ilkine doğru tekrarlanır. Tekrarlanan Parça Modu
TileMode.Mirror: Kenar, son renkten ilkine doğru yansıtılır. TileMode Aynası
TileMode.Clamp: Kenar nihai renge sabitlenir. Ardından, bölgenin geri kalanına en yakın rengi boyuyor. Karo Modu Kıskacı
TileMode.Decal: Yalnızca sınırların boyutuna kadar oluşturur. TileMode.Decal, orijinal sınırların dışındaki içeriği örneklemek için şeffaf siyahı kullanırken TileMode.Clamp, kenar rengini örnekler. Karo Modu Çıkartması

TileMode, diğer yönlü renk geçişlerinde benzer şekilde çalışır. Aradaki fark, tekrarın oluştuğu yöndür.

Fırça Boyutunu Değiştir

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

Çizim alanınızın boyutunu bilmiyorsanız (örneğin, Brush Metin'e atanmışsa) Shader işlevini 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)
)

4'e bölünmüş gölgelendirici boyutu
Şekil 3: Gölgelendirici boyutunun 4'e bölümü

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 gelir. Bu, dairesel renk geçişinin merkezinin daha küçük boyutun (genişlik veya yükseklik) merkezi olarak görünmesini sağlar:

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

Boyut değişikliği içermeyen dairesel renk geçişi grubu
Şekil 4: Boyut değişikliği içermeyen dairesel Gradyan grubu

Dairesel renk geçişi, yarıçap boyutunu maksimum boyuta ayarlamak için değiştirildiğinde, daha iyi bir dairesel renk geçişi efekti ürettiğini 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 göre dairesel gradyan üzerinde daha büyük yarıçap
Şekil 5: Alanın boyutuna bağlı olarak dairesel gradyan üzerinde daha büyük yarıçap

Gölgelendiricinin oluşturulması için geçirilen gerçek boyutun, çağrıldığı yerden belirlendiğini belirtmekte fayda vardır. Varsayılan olarak, boyut, Brush öğesinin son oluşturma işleminden farklıysa veya gölgelendiricinin oluşturulmasında kullanılan bir durum nesnesi değiştiyse Brush, Shader öğesinin dahili olarak yeniden tahsisini yapar.

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

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 komut, şu sonucu verir:

ImageShader Fırçası farklı şekillerde kullanılıyor
Şekil 6: ImageShader Brush'ı kullanarak arka plan, metin ve daire çizme

Metnin artık metnin piksellerini boyamak için ImageBitmap kullanılarak oluşturulduğuna da 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 öncelikle 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 olan mesafeyi (vec2(0, 1)) hesaplar ve mesafeye dayanarak iki renk arasında bir mix yapar. Bu, bir gradyan efekti üretir.

Ardından Gölgelendirici Fırçası'nı oluşturup resolution için üniformaları (çizim alanının boyutu ve özel renk geçişinize giriş olarak kullanmak istediğiniz color ile color2) 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 aşağıdaki işlemin oluşturulduğunu görebilirsiniz:

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

Gölgelendiricilerle, yalnızca renk geçişlerinden çok daha fazlasını yapabileceğinizi de belirtmek gerekir, çünkü bu hesaplamaların tümü matematik tabanlı hesaplamalardır. AGSL hakkında daha fazla bilgi için AGSL belgelerine göz atın.

Ek kaynaklar

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