ConstraintLayout
là một bố cục cho phép bạn đặt các thành phần kết hợp tương ứng với các thành phần kết hợp khác trên màn hình. Đây là giải pháp thay thế cho việc sử dụng nhiều phần tử bố cục lồng nhau Row
, Column
, Box
và các phần tử bố cục tuỳ chỉnh khác. ConstraintLayout
rất hữu ích khi triển khai bố cục lớn hơn với các yêu cầu căn chỉnh phức tạp hơn.
Hãy cân nhắc sử dụng ConstraintLayout
trong các trường hợp sau:
- Để tránh lồng nhiều
Column
vàRow
cho các phần tử định vị trên màn hình nhằm cải thiện khả năng đọc mã. - Định vị các thành phần kết hợp tương ứng với các thành phần kết hợp khác hoặc để định vị các thành phần kết hợp dựa trên nguyên tắc, rào cản hoặc chuỗi.
Trong hệ thống Khung hiển thị, bạn nên sử dụng ConstraintLayout
để tạo các bố cục lớn và phức tạp, vì hệ phân cấp khung hiển thị phẳng hiệu quả hơn khung hiển thị lồng nhau. Tuy nhiên, điều này không phải là vấn đề đáng lo ngại trong Compose vì tính năng này có thể xử lý hiệu quả các hệ phân cấp bố cục sâu.
Bắt đầu với ConstraintLayout
Để sử dụng ConstraintLayout
trong Compose, bạn cần thêm phần phụ thuộc này vào build.gradle
(ngoài chế độ thiết lập Compose):
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
ConstraintLayout
trong Compose hoạt động theo cách bên dưới bằng cách sử dụng
DSL:
- Tạo tệp đối chiếu cho từng thành phần có thể kết hợp trong
ConstraintLayout
bằngcreateRefs()
hoặccreateRefFor()
- Các giới hạn được cung cấp bằng cách sử dụng công cụ sửa đổi
constrainAs()
, lấy tham chiếu làm tham số và cho phép bạn chỉ định các giới hạn của hệ thống trong phần thân lambda. - Bạn có thể chỉ định các quy tắc ràng buộc bằng cách sử dụng
linkTo()
hoặc các phương pháp hữu ích khác. parent
là một tệp đối chiếu hiện có thể dùng để chỉ định các giới hạn cho chính thành phần kết hợpConstraintLayout
.
Dưới đây là ví dụ về một thành phần kết hợp sử dụng ConstraintLayout
:
@Composable fun ConstraintLayoutContent() { ConstraintLayout { // Create references for the composables to constrain val (button, text) = createRefs() Button( onClick = { /* Do something */ }, // Assign reference "button" to the Button composable // and constrain it to the top of the ConstraintLayout modifier = Modifier.constrainAs(button) { top.linkTo(parent.top, margin = 16.dp) } ) { Text("Button") } // Assign reference "text" to the Text composable // and constrain it to the bottom of the Button composable Text( "Text", Modifier.constrainAs(text) { top.linkTo(button.bottom, margin = 16.dp) } ) } }
Mã này ràng buộc phần đầu của Button
thành phần tử cha có lề 16.dp
và một phần Text
ở dưới cùng Button
cũng với lề 16.dp
.
API đã phân tách
Trong ví dụ ConstraintLayout
, các giới hạn được chỉ định cùng dòng, kèm theo đối tượng sửa đổi trong các tệp đối chiếu mà các quy tắc đó được áp dụng. Tuy nhiên, có thể có những trường hợp bạn nên tách riêng các giới hạn khỏi bố cục áp dụng. Ví dụ: bạn có thể thay đổi các giới hạn dựa trên cấu hình màn hình hoặc tạo ảnh động giữa 2 tập hợp các giới hạn.
Đối với các trường hợp như vậy, bạn có thể sử dụng ConstraintLayout
theo cách khác.
- Truyền vào
ConstraintSet
dưới dạng tham số choConstraintLayout
. - Chỉ định các tệp đối chiếu được tạo trong
ConstraintSet
cho các tệp sáng tạo bằng cách sử dụng đối tượng sửa đổilayoutId
.
@Composable fun DecoupledConstraintLayout() { BoxWithConstraints { val constraints = if (minWidth < 600.dp) { decoupledConstraints(margin = 16.dp) // Portrait constraints } else { decoupledConstraints(margin = 32.dp) // Landscape constraints } ConstraintLayout(constraints) { Button( onClick = { /* Do something */ }, modifier = Modifier.layoutId("button") ) { Text("Button") } Text("Text", Modifier.layoutId("text")) } } } private fun decoupledConstraints(margin: Dp): ConstraintSet { return ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") constrain(button) { top.linkTo(parent.top, margin = margin) } constrain(text) { top.linkTo(button.bottom, margin) } } }
Sau đó, khi cần thay đổi các giới hạn, bạn có thể truyền một ConstraintSet
khác.
Khái niệm ConstraintLayout
ConstraintLayout
chứa các khái niệm như nguyên tắc, rào cản và chuỗi có thể giúp xác định vị trí thành phần bên trong Thành phần kết hợp.
Nguyên tắc
Nguyên tắc là các trình trợ giúp trực quan nhỏ để thiết kế bố cục. Các thành phần kết hợp có thể bị hạn chế theo nguyên tắc. Nguyên tắc này hữu ích khi cần xác định vị trí các phần tử ở một dp
hoặc percentage
nhất định bên trong thành phần kết hợp cha.
Có 2 loại nguyên tắc khác nhau là dọc và ngang. 2 chiều ngang là top
và bottom
, và 2 chiều dọc là start
và end
.
ConstraintLayout { // Create guideline from the start of the parent at 10% the width of the Composable val startGuideline = createGuidelineFromStart(0.1f) // Create guideline from the end of the parent at 10% the width of the Composable val endGuideline = createGuidelineFromEnd(0.1f) // Create guideline from 16 dp from the top of the parent val topGuideline = createGuidelineFromTop(16.dp) // Create guideline from 16 dp from the bottom of the parent val bottomGuideline = createGuidelineFromBottom(16.dp) }
Để tạo một nguyên tắc, hãy dùng createGuidelineFrom*
với loại nguyên tắc bắt buộc. Thao tác này sẽ tạo một tệp đối chiếu có thể dùng trong khối Modifier.constrainAs()
.
Rào cản
Các rào cản tham chiếu nhiều thành phần kết hợp để tạo nguyên tắc ảo dựa trên tiện ích cực đoan nhất ở phía được chỉ định.
Để tạo rào cản, hãy sử dụng createTopBarrier()
(hoặc createBottomBarrier()
, createEndBarrier()
, createStartBarrier()
) và cung cấp các tệp đối chiếu sẽ tạo nên rào cản.
ConstraintLayout { val constraintSet = ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") val topBarrier = createTopBarrier(button, text) } }
Sau đó, bạn có thể sử dụng thanh chắn trong khối Modifier.constrainAs()
.
Chuỗi
Chuỗi cung cấp hành vi giống nhóm trong một trục (theo chiều ngang hoặc chiều dọc). Trục còn lại có thể bị hạn chế một cách độc lập.
Để tạo chuỗi, hãy sử dụng createVerticalChain
hoặc createHorizontalChain
:
ConstraintLayout { val constraintSet = ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") val verticalChain = createVerticalChain(button, text, chainStyle = ChainStyle.Spread) val horizontalChain = createHorizontalChain(button, text) } }
Sau đó, bạn có thể sử dụng chuỗi này trong khối Modifier.constrainAs()
.
Bạn có thể định cấu hình chuỗi bằng các ChainStyles
khác nhau để quyết định cách xử lý không gian xung quanh một thành phần kết hợp, chẳng hạn như:
ChainStyle.Spread
: Không gian được phân bổ đồng đều trên tất cả các thành phần kết hợp, bao gồm cả không gian trống trước thành phần kết hợp đầu tiên và sau thành phần kết hợp cuối cùng.ChainStyle.SpreadInside
: Không gian được phân bổ đồng đều trên tất cả các thành phần kết hợp mà không có không gian trống trước thành phần kết hợp đầu tiên hoặc sau thành phần kết hợp cuối cùng.ChainStyle.Packed
: Không gian được phân bổ trước thành phần kết hợp đầu tiên và sau thành phần kết hợp cuối cùng, các thành phần kết hợp được gói với nhau mà không có khoảng trống ở giữa.
Tìm hiểu thêm
Tìm hiểu thêm về ConstraintLayout
trong Compose qua các API đang hoạt động ở phần Mẫu Compose được sử dụng ConstraintLayout
.
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Tiêu điểm trong Compose
- Kotlin cho Jetpack Compose
- Kiến thức cơ bản về bố cục Compose