Brush
trong Compose mô tả cách vẽ một thứ gì đó trên màn hình: yếu tố này xác định (các) màu được vẽ trong khu vực vẽ (tức là một hình tròn, hình vuông, đường dẫn). Có một số Bút vẽ tích hợp hữu ích cho việc vẽ, chẳng hạn như LinearGradient
, RadialGradient
hoặc một bút vẽ SolidColor
thuần tuý.
Bạn có thể sử dụng bút vẽ với các hàm gọi vẽ Modifier.background()
, TextStyle
hoặc DrawScope
để áp dụng kiểu vẽ cho nội dung đang vẽ.
Ví dụ: bạn có thể áp dụng bút vẽ chuyển màu (gradient) theo chiều ngang để vẽ một hình tròn trong DrawScope
:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
Bút vẽ chuyển màu
Bạn có thể sử dụng nhiều bút vẽ chuyển màu tích hợp sẵn để đạt được các hiệu ứng chuyển màu khác nhau. Những bút vẽ này cho phép bạn chỉ định danh sách màu sắc mà bạn muốn tạo hiệu ứng chuyển màu.
Danh sách các bút vẽ chuyển màu có sẵn và kết quả tương ứng:
Loại bút vẽ chuyển màu | Kết quả |
---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
Lưu ý: Để chuyển đổi mượt mà giữa các màu, hãy đặt màu cuối thành màu bắt đầu. |
|
Brush.radialGradient(colorList) |
Thay đổi cách phân bổ màu bằng colorStops
Để tuỳ chỉnh cách màu xuất hiện trong hiệu ứng chuyển màu, bạn có thể điều chỉnh giá trị colorStops
cho từng màu. colorStops
phải được chỉ định dưới dạng phân số, nằm trong khoảng từ 0 đến 1. Các giá trị lớn hơn 1 sẽ dẫn đến việc các màu đó không hiển thị trong hiệu ứng chuyển màu.
Bạn có thể định cấu hình điểm dừng màu (color stop) để có số lượng khác nhau, chẳng hạn như ít hơn hoặc nhiều hơn một màu:
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)) )
Các màu được phân tán ở độ lệch đã cho như được xác định trong cặp colorStop
, ít màu vàng hơn so với màu đỏ và màu xanh dương.
Lặp lại mẫu bằng TileMode
Mỗi bút vẽ chuyển màu có thể tuỳ ý đặt một TileMode
trên đó. Bạn có thể không nhận thấy TileMode
nếu chưa đặt điểm bắt đầu và kết thúc cho hiệu ứng chuyển màu vì theo mặc định, màu này sẽ lấp đầy toàn bộ khu vực. TileMode
sẽ chỉ xếp kề hiệu ứng chuyển màu nếu kích thước của vùng đó lớn hơn kích thước Bút vẽ.
Mã sau sẽ lặp lại mẫu chuyển màu 4 lần, vì endX
được đặt thành 50.dp
và kích thước được đặt thành 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 ) ) )
Bảng sau đây trình bày chi tiết hoạt động của các Chế độ xếp kề (Tile Mode) cho ví dụ HorizontalGradient
ở trên:
TileMode | Đầu ra |
---|---|
TileMode.Repeated : Viền được lặp lại từ màu cuối cùng đến màu đầu tiên. |
|
TileMode.Mirror : Viền được phản chiếu từ màu cuối cùng đến màu đầu tiên. |
|
TileMode.Clamp : Viền được gắn với màu cuối cùng. Sau đó, công cụ này sẽ vẽ màu gần nhất cho phần khu vực còn lại. |
|
TileMode.Decal : Chỉ hiển thị tối đa bằng kích thước của ranh giới. TileMode.Decal tận dụng màu đen trong suốt để lấy mẫu nội dung bên ngoài ranh giới ban đầu, trong khi TileMode.Clamp lấy mẫu màu viền. |
TileMode
hoạt động theo cách tương tự như các chế độ chuyển màu theo hướng, chỉ khác là hướng sẽ lặp lại.
Thay đổi kích thước bút vẽ
Nếu biết kích thước của vùng sẽ được vẽ, bạn có thể đặt ô endX
như chúng ta thấy trong phần TileMode
. Nếu đang ở DrawScope
, bạn có thể sử dụng thuộc tính size
để lấy kích thước của vùng đó.
Nếu không biết kích thước của vùng được vẽ (ví dụ: nếu Brush
được chỉ định cho Văn bản), bạn có thể mở rộng Shader
và sử dụng kích thước của vùng được vẽ trong hàm createShader
.
Trong ví dụ này, hãy chia kích thước cho 4 để lặp lại mẫu 4 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) )
Bạn cũng có thể thay đổi kích thước bút vẽ của mọi chế độ chuyển màu khác, chẳng hạn như chuyển màu dạng hình tròn. Nếu bạn không chỉ định kích thước và tâm, thì hiệu ứng chuyển màu sẽ chiếm toàn bộ ranh giới của DrawScope
và tâm của hiệu ứng chuyển màu dạng hình tròn mặc định là tâm của ranh giới DrawScope
. Điều này dẫn đến tâm của hiệu ứng chuyển màu dạng hình tròn xuất hiện dưới dạng tâm của kích thước nhỏ hơn (chiều rộng hoặc chiều cao):
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
Khi thay đổi chế độ chuyển màu dạng hình tròn để đặt kích thước bán kính thành kích thước tối đa, bạn có thể thấy chế độ này sẽ tạo ra hiệu ứng chuyển màu dạng hình tròn tốt hơn:
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) )
Lưu ý rằng kích thước thực tế được chuyển vào khi tạo chương trình đổ bóng được xác định từ vị trí gọi. Theo mặc định, Brush
sẽ phân bổ lại Shader
trong nội bộ nếu kích thước khác với lần tạo Brush
gần đây nhất hoặc nếu một đối tượng trạng thái được dùng khi tạo chương trình đổ bóng đã thay đổi.
Mã sau đây tạo chương trình đổ bóng 3 lần với các kích thước khác nhau, khi kích thước của vùng được vẽ thay đổi:
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) } } } )
Dùng hình ảnh làm bút vẽ
Để sử dụng ImageBitmap làm Brush
, hãy tải hình ảnh lên dưới dạng ImageBitmap
và tạo một bút vẽ 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))
Bút vẽ được áp dụng cho một số loại bản vẽ: nền, Văn bản và Canvas. Kết quả như sau:
Lưu ý rằng giờ đây, văn bản cũng được hiển thị bằng cách dùng ImageBitmap
để vẽ các điểm ảnh cho văn bản.
Ví dụ nâng cao: Bút vẽ tuỳ chỉnh
Bút vẽ RuntimeShader
AGSL
AGSL cung cấp một tập con chức năng của chương trình đổ bóng GLSL. Bạn có thể viết chương trình đổ bóng bằng AGSL và sử dụng cùng với Bút vẽ trong Compose.
Để tạo Bút vẽ chương trình đổ bóng, trước tiên, hãy xác định Chương trình đổ bóng là chuỗi chương trình đổ bóng 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()
Chương trình đổ bóng ở trên lấy 2 màu đầu vào, tính khoảng cách từ dưới cùng bên trái (vec2(0, 1)
) của vùng được vẽ và thực hiện mix
giữa 2 màu dựa trên khoảng cách. Thao tác này sẽ tạo ra hiệu ứng chuyển màu.
Sau đó, hãy tạo Bút vẽ chương trình đổ bóng và đặt kiểu đồng nhất cho resolution
– kích thước của vùng vẽ và color
cũng như color2
mà bạn muốn dùng làm màu đầu vào cho hiệu ứng chuyển màu tuỳ chỉnh của mình:
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) ) }
Khi chạy phương thức này, bạn có thể thấy các thông tin sau đây hiển thị trên màn hình:
Lưu ý rằng bạn có thể làm được nhiều việc hơn với chương trình đổ bóng thay vì chỉ sử dụng tính năng chuyển màu vì đó là mọi phép tính dựa trên toán học. Để biết thêm thông tin về AGSL, vui lòng xem tài liệu về AGSL.
Tài nguyên khác
Để biết thêm ví dụ về cách sử dụng Bút vẽ trong Compose, hãy xem các tài nguyên sau:
- Tạo ảnh động cho màu văn bản bằng bút vẽ trong Compose 🖌️
- Đồ hoạ và bố cục tuỳ chỉnh trong Compose – Hội nghị Nhà phát triển Android 2022
- Mẫu LaLagged – Bút vẽ RuntimeShader
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Đối tượng sửa đổi đồ hoạ
- Đồ hoạ trong Compose
- Định kiểu văn bản