Đối tượng sửa đổi (modifier) cho phép bạn trang trí hoặc tăng cường hoạt động của thành phần kết hợp. Đối tượng sửa đổi cho phép bạn thực hiện những việc sau:
- Thay đổi kích thước, bố cục, hành vi và giao diện của thành phần kết hợp
- Thêm thông tin, như nhãn hỗ trợ tiếp cận
- Xử lý dữ liệu do người dùng nhập
- Thêm các lượt tương tác cấp cao, như làm cho một thành phần có thể nhấp vào, cuộn được, có thể kéo hoặc thu phóng
Đối tượng sửa đổi là các đối tượng Kotlin chuẩn. Tạo một đối tượng sửa đổi bằng cách gọi một trong các hàm lớp Modifier
:
@Composable private fun Greeting(name: String) { Column(modifier = Modifier.padding(24.dp)) { Text(text = "Hello,") Text(text = name) } }
Bạn có thể liên kết các hàm này với nhau để kết hợp chúng:
@Composable private fun Greeting(name: String) { Column( modifier = Modifier .padding(24.dp) .fillMaxWidth() ) { Text(text = "Hello,") Text(text = name) } }
Trong mã trên, hãy chú ý đến các hàm khác nhau của đối tượng sửa đổi được sử dụng cùng nhau.
padding
đặt khoảng trống xung quanh một phần tử.fillMaxWidth
làm cho thành phần kết hợp lấp đầy chiều rộng tối đa mà thành phần mẹ đã cấp cho nó.
Phương pháp hay nhất là khiến tất cả các thành phần kết hợp đều chấp nhận tham số modifier
và chuyển đối tượng sửa đổi đó đến phần tử con đầu tiên tạo ra giao diện người dùng.
Khi làm như vậy, mã của bạn có thể sử dụng lại dễ dàng hơn và làm cho hành vi của mã dễ dự đoán và trực quan hơn. Để biết thêm thông tin, hãy xem nguyên tắc về API Compose, Các thành phần chấp nhận và tuân theo một tham số của Đối tượng sửa đổi.
Thứ tự của các đối tượng sửa đổi rất quan trọng
Thứ tự các hàm đối tượng sửa đổi là rất quan trọng. Do mỗi hàm thực hiện các thay đổi cho Modifier
do hàm trước đó trả về nên trình tự sẽ ảnh hưởng đến kết quả cuối cùng. Chúng ta hãy cùng xem một ví dụ về cách này:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { // rest of the implementation } }
Trong mã phía trên toàn bộ khu vực, có thể nhấp vào, bao gồm cả các khoảng đệm xung quanh, bởi vì đối tượng sửa đổi padding
đã được áp dụng sau đối tượng sửa đổi clickable
. Nếu thứ tự các đối tượng sửa đổi bị đảo ngược, thì dấu cách do padding
thêm sẽ không phản ứng với thao tác nhập của người dùng:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .padding(padding) .clickable(onClick = onClick) .fillMaxWidth() ) { // rest of the implementation } }
Đối tượng sửa đổi tích hợp
Jetpack Compose cung cấp một danh sách các đối tượng sửa đổi tích hợp để giúp bạn trang trí hoặc tích hợp một thành phần kết hợp. Dưới đây là một số đối tượng sửa đổi phổ biến mà bạn sẽ sử dụng để điều chỉnh bố cục của mình.
padding
và size
Theo mặc định, các bố cục có trong Compose sẽ gói phần tử con. Tuy nhiên, bạn có thể đặt kích thước bằng cách sử dụng đối tượng sửa đổi size
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image(/*...*/) Column { /*...*/ } } }
Lưu ý rằng kích thước bạn chỉ định có thể không được tuân thủ nếu kích thước không đáp ứng các ràng buộc đến từ nguồn gốc của bố cục. Nếu bạn yêu cầu cố định kích thước của thành phần kết hợp bất kể các hạn chế đến, hãy sử dụng đối tượng sửa đổi requiredSize
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.requiredSize(150.dp) ) Column { /*...*/ } } }
Trong ví dụ này, ngay cả khi phần tử mẹ height
được đặt thành 100.dp
, chiều cao của Image
sẽ là 150.dp
, vì đối tượng sửa đổi requiredSize
được ưu tiên.
Nếu bạn muốn bố cục con lấp đầy tất cả chiều cao mà phần tử mẹ cho phép, hãy thêm đối tượng sửa đổi fillMaxHeight
(Compose cũng cung cấp fillMaxSize
và fillMaxWidth
):
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.fillMaxHeight() ) Column { /*...*/ } } }
Để thêm khoảng đệm xung quanh một phần tử, hãy đặt một đối tượng sửa đổi padding
.
Nếu bạn muốn thêm khoảng đệm phía trên một đường cơ sở của văn bản để đạt được một khoảng cách cụ thể từ đầu bố cục đến đường cơ sở, hãy sử dụng đối tượng sửa đổi paddingFromBaseline
.
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text( text = artist.name, modifier = Modifier.paddingFromBaseline(top = 50.dp) ) Text(artist.lastSeenOnline) } } }
Độ lệch
Để định vị một bố cục so với vị trí ban đầu, hãy thêm đối tượng sửa đổi offset
và đặt độ lệch trong trục x và y.
Mức chênh lệch có thể mang giá trị dương hoặc không dương. Sự khác biệt giữa padding
và offset
là việc thêm offset
vào một thành phần kết hợp không thay đổi hoạt động đo lường:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text(artist.name) Text( text = artist.lastSeenOnline, modifier = Modifier.offset(x = 4.dp) ) } } }
Đối tượng sửa đổi offset
được áp dụng theo chiều ngang theo hướng bố cục.
Trong ngữ cảnh từ trái sang phải, một offset
dương chuyển phần tử sang bên phải, trong khi trong ngữ cảnh từ phải sang trái, thuộc tính này dịch chuyển phần tử sang trái.
Nếu bạn cần đặt một mức chênh lệch mà không xem xét đến hướng bố cục, hãy xem đối tượng sửa đổi absoluteOffset
, trong đó giá trị chênh lệch dương luôn chuyển phần tử sang bên phải.
Đối tượng sửa đổi offset
cung cấp 2 phương thức nạp chồng – offset
lấy mức chênh lệch làm tham số và offset
lấy giá trị lambda.
Để biết thêm thông tin chuyên sâu về thời điểm sử dụng từng đối tượng này và cách tối ưu hoá hiệu suất, hãy đọc phần Hiệu suất của Compose – Trì hoãn việc đọc càng lâu càng tốt.
Phạm vi an toàn trong Compose
Trong Compose có các đối tượng sửa đổi chỉ có thể dùng khi áp dụng cho thành phần con của một số thành phần kết hợp cụ thể. Compose thực thi việc này bằng phạm vi tuỳ chỉnh.
Ví dụ: nếu bạn muốn cho thành phần con lớn bằng thành phần mẹ Box
mà không ảnh hưởng đến kích thước của Box
, hãy dùng đối tượng sửa đổi matchParentSize
. matchParentSize
chỉ có trong BoxScope
.
Do đó, bạn chỉ có thể dùng đối tượng này cho thành phần con trong thành phần mẹ Box
.
Phạm vi an toàn ngăn bạn thêm đối tượng sửa đổi không hoạt động trong các thành phần kết hợp và phạm vi khác, đồng thời giúp tiết kiệm thời gian thử và sai.
Đối tượng sửa đổi có giới hạn thông báo cho yếu tố mẹ về một số thông tin mà thành phần mẹ cần biết về thành phần con. Đây thường được gọi là đối tượng sửa đổi dữ liệu gốc. Bên trong chúng khác với đối tượng sửa đổi mục đích chung, nhưng xét về góc độ sử dụng thì những khác biệt này không quan trọng.
matchParentSize
trong Box
Như đã đề cập ở trên, nếu bạn muốn bố cục con có cùng kích thước với bố cục mẹ Box
mà không ảnh hưởng đến kích thước Box
, hãy sử dụng đối tượng sửa đổi matchParentSize
.
Xin lưu ý rằng matchParentSize
chỉ có trong phạm vi Box
, có nghĩa là chỉ áp dụng cho phần tử con trực tiếp của thành phần kết hợp Box
.
Trong ví dụ dưới đây, thành phần con Spacer
có kích thước từ thành phần mẹ Box
, điều này lấy kích thước từ thành phần con lớn nhất, ArtistCard
trong trường hợp này.
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
Nếu fillMaxSize
được sử dụng thay cho matchParentSize
, thì Spacer
sẽ nhận tất cả không gian trống có sẵn cho mạng gốc, đến lượt thành phần mẹ sẽ mở rộng và lấp đầy tất cả không gian có sẵn.
weight
trong Row
và Column
Như bạn đã thấy trong phần trước về Khoảng đệm và kích thước, theo mặc định, kích thước của thành phần kết hợp được xác định bởi nội dung mà phần tử này gói lại. Bạn có thể đặt kích thước của thành phần kết hợp để linh hoạt trong phần tử gốc bằng cách sử dụng Đối tượng sửa đổi weight
chỉ có trong RowScope
và ColumnScope
.
Hãy lấy một Row
chứa 2 thành phần kết hợp Box
.
Hộp đầu tiên được gán 2 lần weight
của giây thứ hai, do đó, hộp này được gấp đôi chiều rộng. Vì Row
rộng 210.dp
, Box
đầu tiên có chiều rộng 140.dp
và giá trị thứ hai là 70.dp
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.fillMaxWidth() ) { Image( /*...*/ modifier = Modifier.weight(2f) ) Column( modifier = Modifier.weight(1f) ) { /*...*/ } } }
Trích xuất và sử dụng lại đối tượng sửa đổi
Bạn có thể nhóm nhiều đối tượng sửa đổi với nhau để trang trí hoặc tích hợp một thành phần kết hợp. Chuỗi này được tạo thông qua giao diện Modifier
, đại diện cho danh sách đơn lẻ không thể thay đổi của Modifier.Elements
.
Mỗi Modifier.Element
đại diện cho một hành vi riêng lẻ, như bố cục, vẽ và đồ hoạ, tất cả hành vi liên quan đến cử chỉ, tiêu điểm và ngữ nghĩa, cũng như các sự kiện nhập thiết bị. Thứ tự của các phần tử này rất quan trọng: các phần tử sửa đổi được thêm vào trước sẽ được áp dụng đầu tiên.
Đôi khi, việc sử dụng lại các thực thể chuỗi đối tượng sửa đổi trong nhiều thành phần kết hợp có thể mang lại lợi ích, bằng cách trích xuất các phiên bản đó thành các biến và di chuyển chúng lên các phạm vi cao hơn. Tính năng này có thể cải thiện khả năng đọc mã hoặc giúp cải thiện hiệu suất của ứng dụng vì một số lý do:
- Quá trình phân bổ lại của đối tượng sửa đổi sẽ không được lặp lại khi quá trình kết hợp lại diễn ra cho các thành phần kết hợp có thể sử dụng
- Các chuỗi đối tượng sửa đổi có thể rất dài và phức tạp, vì vậy, việc sử dụng lại cùng một phiên bản của một chuỗi có thể giảm bớt thời gian chạy khối lượng công việc mà Compose cần thực hiện khi so sánh các chuỗi đó
- Hoạt động trích xuất này giúp tăng độ sạch sẽ, tính nhất quán và khả năng bảo trì của mã trên cơ sở mã
Các phương pháp hay nhất để sử dụng lại đối tượng sửa đổi
Tạo chuỗi Modifier
của riêng bạn và trích xuất các chuỗi đó để sử dụng lại trên nhiều thành phần kết hợp. Bạn chỉ cần lưu một đối tượng sửa đổi, vì công cụ này là các đối tượng giống dữ liệu:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
Trích xuất và sử dụng lại đối tượng sửa đổi khi quan sát thấy trạng thái thường xuyên thay đổi
Khi bạn quan sát thấy các trạng thái thường xuyên thay đổi bên trong các thành phần kết hợp, như trạng thái động hoặc scrollState
, có thể sẽ có một lượng tái cấu trúc đáng kể. Trong trường hợp này, đối tượng sửa đổi sẽ được phân bổ cho mọi quá trình kết hợp lại và có thể cho mọi khung:
@Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // Creation and allocation of this modifier will happen on every frame of the animation! modifier = Modifier .padding(12.dp) .background(Color.Gray), animatedState = animatedState ) }
Thay vào đó, bạn có thể tạo, trích xuất và sử dụng lại cùng một phiên bản của đối tượng sửa đổi và chuyển phiên bản đó cho thành phần kết hợp như sau:
// Now, the allocation of the modifier happens here: val reusableModifier = Modifier .padding(12.dp) .background(Color.Gray) @Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // No allocation, as we're just reusing the same instance modifier = reusableModifier, animatedState = animatedState ) }
Trích xuất và sử dụng lại đối tượng sửa đổi không theo phạm vi
Các đối tượng sửa đổi có thể không theo phạm vi hoặc thuộc phạm vi của thành phần kết hợp cụ thể. Trong trường hợp đối tượng sửa đổi không theo phạm vi, bạn có thể dễ dàng trích xuất các đối tượng sửa đổi đó bên ngoài bất kỳ thành phần kết hợp nào dưới dạng biến đơn giản:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) @Composable fun AuthorField() { HeaderText( // ... modifier = reusableModifier ) SubtitleText( // ... modifier = reusableModifier ) }
Điều này đặc biệt có lợi khi kết hợp với bố cục Lazy (Tải từng phần). Trong hầu hết các trường hợp, bạn sẽ muốn tất cả các mục có khả năng quan trọng có cùng một đối tượng sửa đổi:
val reusableItemModifier = Modifier .padding(bottom = 12.dp) .size(216.dp) .clip(CircleShape) @Composable private fun AuthorList(authors: List<Author>) { LazyColumn { items(authors) { AsyncImage( // ... modifier = reusableItemModifier, ) } } }
Trích xuất và sử dụng lại đối tượng sửa đổi theo phạm vi
Khi xử lý các đối tượng sửa đổi trong phạm vi của một số thành phần kết hợp nhất định, bạn có thể trích xuất các đối tượng sửa đổi đó đến mức cao nhất có thể và sử dụng lại nếu thích hợp:
Column(/*...*/) { val reusableItemModifier = Modifier .padding(bottom = 12.dp) // Align Modifier.Element requires a ColumnScope .align(Alignment.CenterHorizontally) .weight(1f) Text1( modifier = reusableItemModifier, // ... ) Text2( modifier = reusableItemModifier // ... ) // ... }
Bạn chỉ nên truyền đối tượng sửa đổi được trích xuất và trong phạm vi đến thành phần con trực tiếp có cùng phạm vi. Hãy xem phần Phạm vi an toàn trong Compose để tham khảo thêm về lý do lý do tại sao điều này lại quan trọng:
Column(modifier = Modifier.fillMaxWidth()) { // Weight modifier is scoped to the Column composable val reusableItemModifier = Modifier.weight(1f) // Weight will be properly assigned here since this Text is a direct child of Column Text1( modifier = reusableItemModifier // ... ) Box { Text2( // Weight won't do anything here since the Text composable is not a direct child of Column modifier = reusableItemModifier // ... ) } }
Nối dài chuỗi các đối tượng sửa đổi được trích xuất
Bạn có thể liên kết thêm hoặc thêm các chuỗi đối tượng sửa đổi được trích xuất bằng cách gọi hàm .then()
:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) // Append to your reusableModifier reusableModifier.clickable { /*...*/ } // Append your reusableModifier otherModifier.then(reusableModifier)
Bạn chỉ cần lưu ý rằng thứ tự của các đối tượng sửa đổi rất quan trọng!
Tìm hiểu thêm
Chúng tôi cung cấp một danh sách đầy đủ các đối tượng sửa đổi, cùng với các tham số và phạm vi của đối tượng sửa đổi.
Để thực hành thêm về cách sử dụng đối tượng sửa đổi, bạn cũng có thể xem Lớp học lập trình Bố cục cơ bản trong Compose và tham khảo Hiện có trong kho lưu trữ Android.
Để biết thêm thông tin về đối tượng sửa đổi tuỳ chỉnh và cách tạo chúng, hãy xem tài liệu về Bố cục tuỳ chỉnh – Sử dụng đối tượng sửa đổi bố cục.
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Kiến thức cơ bản về bố cục Compose
- Thao tác trong trình chỉnh sửa {:#editor-actions}
- Bố cục tuỳ chỉnh {:#custom-layouts }