A Brush ใน Compose อธิบายวิธีวาดสิ่งต่างๆ บนหน้าจอ โดยจะ
กำหนดสีที่จะวาดในพื้นที่วาด (เช่น วงกลม
สี่เหลี่ยมจัตุรัส เส้นทาง) มี Brush ในตัว 2-3 แบบที่มีประโยชน์สำหรับการวาด
เช่น LinearGradient, RadialGradient หรือ Brush
SolidColor ธรรมดา
คุณสามารถใช้ Brush กับ Modifier.background(), TextStyle หรือ
DrawScope คำสั่งประมวลผล เพื่อใช้สไตล์การระบายสีกับเนื้อหา
ที่วาด
ตัวอย่างเช่น คุณสามารถใช้ Brush การไล่ระดับสีแนวนอนกับการวาดวงกลมใน DrawScope ได้ดังนี้
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
Brush การไล่ระดับสี
มี Brush การไล่ระดับสีในตัวมากมายที่คุณใช้เพื่อสร้างเอฟเฟกต์การไล่ระดับสีต่างๆ ได้ Brush เหล่านี้ช่วยให้คุณระบุรายการสีที่ต้องการใช้สร้างการไล่ระดับสีได้
รายการ Brush การไล่ระดับสีที่ใช้ได้และเอาต์พุตที่เกี่ยวข้องมีดังนี้
| ประเภท Brush การไล่ระดับสี | เอาต์พุต |
|---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
หมายเหตุ: หากต้องการให้สีเปลี่ยนผ่านอย่างราบรื่น ให้ตั้งสีสุดท้ายเป็นสีเริ่มต้น |
|
Brush.radialGradient(colorList) |
|
เปลี่ยนการกระจายสีด้วย colorStops
หากต้องการปรับแต่งลักษณะที่สีปรากฏในการไล่ระดับสี คุณสามารถปรับค่า colorStops ของแต่ละสีได้ โดย colorStops ควรระบุเป็นเศษส่วนระหว่าง 0 ถึง 1 ค่าที่มากกว่า 1 จะทำให้สีเหล่านั้นไม่แสดงเป็นส่วนหนึ่งของการไล่ระดับสี
คุณสามารถกำหนดค่า Color Stop ให้มีปริมาณสีที่แตกต่างกันได้ เช่น สีหนึ่งน้อยกว่าหรือมากกว่าสีอื่น
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)) )
สีจะกระจายไปตามออฟเซ็ตที่ระบุตามที่กำหนดไว้ในคู่ colorStop โดยมีสีเหลืองน้อยกว่าสีแดงและสีน้ำเงิน
ทำซ้ำรูปแบบด้วย TileMode
Brush การไล่ระดับสีแต่ละแบบมีตัวเลือกในการตั้งค่า TileMode คุณอาจไม่สังเกตเห็น TileMode หากไม่ได้ตั้งค่าเริ่มต้นและสิ้นสุดสำหรับการไล่ระดับสี เนื่องจากระบบจะเติมพื้นที่ทั้งหมดโดยค่าเริ่มต้น TileMode จะเรียงต่อกันเฉพาะเมื่อขนาดของพื้นที่ใหญ่กว่าขนาด Brush
โค้ดต่อไปนี้จะทำซ้ำรูปแบบการไล่ระดับสี 4 ครั้ง เนื่องจากตั้งค่า endX เป็น 50.dp และตั้งค่าขนาดเป็น 200.dp
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 ) ) )
ตารางต่อไปนี้แสดงรายละเอียดสิ่งที่โหมดการเรียงต่อกันต่างๆ ทำกับตัวอย่าง HorizontalGradient ด้านบน
| TileMode | เอาต์พุต |
|---|---|
TileMode.Repeated: ขอบจะทำซ้ำจากสีสุดท้ายไปเป็นสีแรก |
|
TileMode.Mirror: ขอบจะสะท้อนจากสีสุดท้ายไปเป็นสีแรก |
|
TileMode.Clamp: ขอบจะยึดกับสีสุดท้าย จากนั้นจะระบายสีที่ใกล้เคียงที่สุดสำหรับพื้นที่ที่เหลือ |
|
TileMode.Decal: แสดงผลเฉพาะขนาดขอบเขต TileMode.Decal ใช้สีดำโปร่งใสเพื่อสุ่มตัวอย่างเนื้อหาภายนอกขอบเขตเดิม ขณะที่ TileMode.Clamp จะสุ่มตัวอย่างสีขอบ |
|
TileMode ทำงานในลักษณะคล้ายกันสำหรับการไล่ระดับสีตามทิศทางอื่นๆ โดยความแตกต่างคือทิศทางที่การทำซ้ำเกิดขึ้น
เปลี่ยนขนาด Brush
หากทราบขนาดของพื้นที่ที่จะวาด Brush คุณสามารถตั้งค่า endX ของการเรียงต่อกันได้ตามที่เห็นในส่วน TileMode ด้านบน หากอยู่ใน
a DrawScope คุณสามารถใช้พร็อพเพอร์ตี้ size เพื่อรับขนาดของพื้นที่ได้
หากไม่ทราบขนาดพื้นที่วาด (เช่น หากกำหนด
Brush ให้กับข้อความ) คุณสามารถขยาย Shader และใช้ขนาดของ
พื้นที่วาดในฟังก์ชัน createShader ได้
ในตัวอย่างนี้ ให้หารขนาดด้วย 4 เพื่อทำซ้ำรูปแบบ 4 ครั้ง
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) )
นอกจากนี้ คุณยังเปลี่ยนขนาด Brush ของการไล่ระดับสีอื่นๆ ได้ด้วย เช่น การไล่ระดับสีแบบรัศมี หากไม่ระบุขนาดและจุดกึ่งกลาง การไล่ระดับสีจะครอบคลุมขอบเขตทั้งหมดของ DrawScope และจุดกึ่งกลางของการไล่ระดับสีแบบรัศมีจะใช้จุดกึ่งกลางของขอบเขต DrawScope เป็นค่าเริ่มต้น ซึ่งจะทำให้จุดกึ่งกลางของการไล่ระดับสีแบบรัศมีปรากฏเป็นจุดกึ่งกลางของขนาดที่เล็กกว่า (ความกว้างหรือความสูง)
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
เมื่อเปลี่ยนการไล่ระดับสีแบบรัศมีเพื่อตั้งค่าขนาดรัศมีเป็นขนาดสูงสุด คุณจะเห็นว่าการไล่ระดับสีแบบรัศมีมีเอฟเฟกต์ที่ดีขึ้น
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) )
โปรดทราบว่าระบบจะกำหนดขนาดจริงที่ส่งไปยังการสร้าง Shader จากตำแหน่งที่เรียกใช้ โดยค่าเริ่มต้น Brush จะจัดสรร Shader ใหม่อีกครั้งภายในหากขนาดแตกต่างจากการสร้าง Brush ครั้งล่าสุด หรือหากออบเจ็กต์สถานะที่ใช้ในการสร้าง Shader เปลี่ยนไป
โค้ดต่อไปนี้จะสร้าง Shader 3 ครั้งด้วยขนาดที่แตกต่างกัน เนื่องจากขนาดของพื้นที่วาดเปลี่ยนไป
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) } } } )
ใช้รูปภาพเป็น Brush
หากต้องการใช้ ImageBitmap เป็น Brush ให้โหลดรูปภาพเป็น ImageBitmap,
แล้วสร้าง Brush ImageShader ดังนี้
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))
ระบบจะใช้ Brush กับการวาด 3 ประเภท ได้แก่ พื้นหลัง ข้อความ และ Canvas ซึ่งจะแสดงผลดังนี้
โปรดสังเกตว่าตอนนี้ข้อความจะแสดงผลโดยใช้ ImageBitmap เพื่อระบายสีพิกเซลสำหรับข้อความด้วย
ตัวอย่างขั้นสูง: Brush ที่กำหนดเอง
Brush RuntimeShader ของ AGSL
AGSL มีความสามารถของ GLSL Shader บางส่วน คุณสามารถเขียน Shader ใน AGSL และใช้กับ Brush ใน Compose ได้
หากต้องการสร้าง Brush Shader ให้กำหนด Shader เป็นสตริง Shader ของ AGSL ก่อน
@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()
Shader ด้านบนใช้สีอินพุต 2 สี คำนวณระยะทางจากด้านซ้ายล่าง (vec2(0, 1)) ของพื้นที่วาด และทำ mix ระหว่าง 2 สีตามระยะทาง ซึ่งจะสร้างเอฟเฟกต์การไล่ระดับสี
จากนั้นสร้าง Brush Shader และตั้งค่า Uniform สำหรับ resolution ซึ่งเป็นขนาด
ของพื้นที่วาด รวมถึง color และ color2 ที่ต้องการใช้เป็นอินพุตสำหรับ
การไล่ระดับสีที่กำหนดเอง
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) ) }
เมื่อเรียกใช้โค้ดนี้ คุณจะเห็นผลลัพธ์ต่อไปนี้แสดงบนหน้าจอ
โปรดทราบว่าคุณสามารถทำสิ่งต่างๆ ได้มากมายด้วย Shader นอกเหนือจากการไล่ระดับสี เนื่องจากทุกอย่างเป็นการคำนวณทางคณิตศาสตร์ ดูข้อมูลเพิ่มเติมเกี่ยวกับ AGSL ได้ที่ เอกสารประกอบของ AGSL
แหล่งข้อมูลเพิ่มเติม
ดูตัวอย่างเพิ่มเติมเกี่ยวกับการใช้ Brush ใน Compose ได้จากแหล่งข้อมูลต่อไปนี้
- การทำให้สีข้อความ Brush เคลื่อนไหวใน Compose 🖌️
- กราฟิกและเลย์เอาต์ที่กำหนดเองใน Compose - Android Dev Summit 2022
- ตัวอย่าง JetLagged - Brush RuntimeShader
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ตัวปรับแต่งกราฟิก
- กราฟิกใน Compose
- จัดรูปแบบข้อความ