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