Trang này mô tả cách xử lý kích thước và cung cấp bố cục linh hoạt và thích ứng bằng Glance, sử dụng các thành phần Glance hiện có.
Sử dụng Box
, Column
và Row
Glance có ba bố cục có thể kết hợp chính:
Box
: Đặt các phần tử lên trên nhau. Hàm này sẽ chuyển thànhRelativeLayout
.Column
: Đặt các phần tử theo thứ tự trong trục dọc. Hàm này sẽ dịch thànhLinearLayout
có hướng dọc.Row
: Đặt các phần tử theo thứ tự trong trục hoành. Hàm này sẽ dịch thànhLinearLayout
theo hướng ngang.
Glance hỗ trợ các đối tượng Scaffold
. Đặt các thành phần kết hợp Column
, Row
và Box
trong một đối tượng Scaffold
nhất định.
Mỗi thành phần kết hợp này cho phép bạn xác định căn chỉnh dọc và ngang của nội dung cũng như các quy tắc ràng buộc về chiều rộng, chiều cao, trọng số hoặc khoảng đệm bằng cách sử dụng đối tượng sửa đổi. Ngoài ra, mỗi thành phần con có thể xác định đối tượng sửa đổi để thay đổi không gian và vị trí bên trong thành phần mẹ.
Ví dụ sau đây cho bạn biết cách tạo một Row
phân phối đều các phần tử con theo chiều ngang, như trong Hình 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Row
lấp đầy chiều rộng tối đa có sẵn và vì mỗi phần tử con có cùng trọng số, nên các phần tử con này chia đều không gian có sẵn. Bạn có thể xác định các trọng số, kích thước, khoảng đệm hoặc căn chỉnh khác nhau để điều chỉnh bố cục cho phù hợp với nhu cầu của mình.
Sử dụng bố cục có thể cuộn
Một cách khác để cung cấp nội dung thích ứng là cho phép cuộn nội dung. Bạn có thể làm điều này bằng thành phần kết hợp LazyColumn
. Thành phần kết hợp này cho phép bạn xác định một nhóm mục sẽ hiển thị bên trong vùng chứa cuộn được trong tiện ích ứng dụng.
Các đoạn mã sau đây cho thấy nhiều cách xác định các mục bên trong LazyColumn
.
Bạn có thể cung cấp số lượng mặt hàng:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
Cung cấp từng mục riêng lẻ:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
Cung cấp danh sách hoặc mảng các mục:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
Bạn cũng có thể kết hợp các ví dụ trước:
LazyColumn { item { Text("Names:") } items(peopleNameList) { name -> Text(name) } // or in case you need the index: itemsIndexed(peopleNameList) { index, person -> Text("$person at index $index") } }
Xin lưu ý rằng đoạn mã trước đó không chỉ định itemId
. Việc chỉ định itemId
giúp cải thiện hiệu suất và duy trì vị trí cuộn thông qua danh sách và các bản cập nhật appWidget
từ Android 12 trở lên (ví dụ: khi thêm hoặc xoá các mục khỏi danh sách). Ví dụ sau đây cho thấy cách chỉ định itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
Xác định SizeMode
Kích thước AppWidget
có thể khác nhau tuỳ thuộc vào thiết bị, lựa chọn của người dùng hoặc trình chạy, vì vậy, điều quan trọng là phải cung cấp bố cục linh hoạt như mô tả trong trang Cung cấp bố cục tiện ích linh hoạt. Glance đơn giản hoá việc này bằng cách xác định SizeMode
và giá trị LocalSize
. Các phần sau đây mô tả 3 chế độ.
SizeMode.Single
SizeMode.Single
là chế độ mặc định. Điều này cho biết rằng chỉ có một loại nội dung được cung cấp; tức là ngay cả khi kích thước có sẵn của AppWidget
thay đổi, kích thước nội dung sẽ không thay đổi.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the minimum size or resizable // size defined in the App Widget metadata val size = LocalSize.current // ... } }
Khi sử dụng chế độ này, hãy đảm bảo rằng:
- Giá trị siêu dữ liệu về kích thước tối thiểu và tối đa được xác định đúng cách dựa trên kích thước nội dung.
- Nội dung đủ linh hoạt trong phạm vi kích thước dự kiến.
Nhìn chung, bạn nên sử dụng chế độ này khi:
a) AppWidget
có kích thước cố định hoặc b) không thay đổi nội dung khi đổi kích thước.
SizeMode.Responsive
Chế độ này tương đương với việc cung cấp bố cục thích ứng, cho phép GlanceAppWidget
xác định một tập hợp bố cục thích ứng được giới hạn bởi các kích thước cụ thể. Đối với mỗi kích thước được xác định, nội dung sẽ được tạo và liên kết với kích thước cụ thể khi AppWidget
được tạo hoặc cập nhật. Sau đó, hệ thống sẽ chọn kích thước phù hợp nhất dựa trên kích thước có sẵn.
Ví dụ: trong AppWidget
đích, bạn có thể xác định ba kích thước và nội dung của kích thước đó:
class MyAppWidget : GlanceAppWidget() { companion object { private val SMALL_SQUARE = DpSize(100.dp, 100.dp) private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp) private val BIG_SQUARE = DpSize(250.dp, 250.dp) } override val sizeMode = SizeMode.Responsive( setOf( SMALL_SQUARE, HORIZONTAL_RECTANGLE, BIG_SQUARE ) ) override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be one of the sizes defined above. val size = LocalSize.current Column { if (size.height >= BIG_SQUARE.height) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) } Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width >= HORIZONTAL_RECTANGLE.width) { Button("School") } } if (size.height >= BIG_SQUARE.height) { Text(text = "provided by X") } } } }
Trong ví dụ trước, phương thức provideContent
được gọi ba lần và được liên kết với kích thước đã xác định.
- Trong lệnh gọi đầu tiên, kích thước sẽ được đánh giá là
100x100
. Nội dung không bao gồm nút bổ sung cũng như văn bản ở đầu và cuối. - Trong lệnh gọi thứ hai, kích thước sẽ được đánh giá là
250x100
. Nội dung bao gồm nút bổ sung nhưng không bao gồm văn bản ở trên cùng và dưới cùng. - Trong lệnh gọi thứ ba, kích thước sẽ có giá trị là
250x250
. Nội dung bao gồm cả nút bổ sung và cả hai văn bản.
SizeMode.Responsive
là sự kết hợp của hai chế độ còn lại và cho phép bạn xác định nội dung thích ứng trong các giới hạn được xác định trước. Nhìn chung, chế độ này hoạt động hiệu quả hơn và cho phép chuyển đổi mượt mà hơn khi AppWidget
được đổi kích thước.
Bảng sau đây cho thấy giá trị của kích thước, tuỳ thuộc vào SizeMode
và kích thước AppWidget
có sẵn:
Kích thước có sẵn | 105 x 110 | 203 x 112 | 72 x 72 | 203 x 150 |
---|---|---|---|---|
SizeMode.Single |
110 x 110 | 110 x 110 | 110 x 110 | 110 x 110 |
SizeMode.Exact |
105 x 110 | 203 x 112 | 72 x 72 | 203 x 150 |
SizeMode.Responsive |
80 x 100 | 80 x 100 | 80 x 100 | 150 x 120 |
* Giá trị chính xác chỉ dùng cho mục đích minh hoạ. |
SizeMode.Exact
SizeMode.Exact
tương đương với việc cung cấp bố cục chính xác, yêu cầu nội dung GlanceAppWidget
mỗi khi kích thước AppWidget
có sẵn thay đổi (ví dụ: khi người dùng đổi kích thước AppWidget
trên màn hình chính).
Ví dụ: trong tiện ích đích, bạn có thể thêm một nút bổ sung nếu chiều rộng có sẵn lớn hơn một giá trị nhất định.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the size of the AppWidget val size = LocalSize.current Column { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width > 250.dp) { Button("School") } } } } }
Chế độ này linh hoạt hơn so với các chế độ khác, nhưng cũng có một số lưu ý:
- Bạn phải tạo lại hoàn toàn
AppWidget
mỗi khi kích thước thay đổi. Điều này có thể dẫn đến các vấn đề về hiệu suất và giao diện người dùng bị nhảy khi nội dung phức tạp. - Kích thước có sẵn có thể khác nhau tuỳ thuộc vào cách triển khai của trình chạy. Ví dụ: nếu trình chạy không cung cấp danh sách kích thước, thì kích thước tối thiểu có thể sẽ được sử dụng.
- Trên các thiết bị chạy Android phiên bản trước 12, logic tính toán kích thước có thể không hoạt động trong mọi trường hợp.
Nhìn chung, bạn nên sử dụng chế độ này nếu không thể sử dụng SizeMode.Responsive
(tức là không thể sử dụng một nhóm nhỏ bố cục thích ứng).
Truy cập tài nguyên
Sử dụng LocalContext.current
để truy cập vào bất kỳ tài nguyên Android nào, như trong ví dụ sau:
LocalContext.current.getString(R.string.glance_title)
Bạn nên cung cấp trực tiếp mã nhận dạng tài nguyên để giảm kích thước của đối tượng RemoteViews
cuối cùng và bật các tài nguyên động, chẳng hạn như màu động.
Các thành phần kết hợp và phương thức chấp nhận tài nguyên bằng cách sử dụng "trình cung cấp", chẳng hạn như ImageProvider
hoặc sử dụng phương thức nạp chồng như GlanceModifier.background(R.color.blue)
. Ví dụ:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Xử lý văn bản
Glance 1.1.0 bao gồm một API để đặt kiểu văn bản. Đặt kiểu văn bản bằng cách sử dụng thuộc tính fontSize
, fontWeight
hoặc fontFamily
của lớp TextStyle.
fontFamily
hỗ trợ tất cả phông chữ hệ thống, như trong ví dụ sau, nhưng không hỗ trợ phông chữ tuỳ chỉnh trong ứng dụng:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
Thêm nút phức hợp
Nút phức hợp được giới thiệu trong Android 12. Glance hỗ trợ khả năng tương thích ngược cho các loại nút phức hợp sau:
Mỗi nút phức hợp này hiển thị một thành phần hiển thị có thể nhấp đại diện cho trạng thái "đã đánh dấu".
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableStateOf(0) } CheckBox( checked = isApplesChecked, onCheckedChange = { isApplesChecked = !isApplesChecked }, text = "Apples" ) Switch( checked = isEnabledSwitched, onCheckedChange = { isEnabledSwitched = !isEnabledSwitched }, text = "Enabled" ) RadioButton( checked = isRadioChecked == 1, onClick = { isRadioChecked = 1 }, text = "Checked" )
Khi trạng thái thay đổi, lambda được cung cấp sẽ được kích hoạt. Bạn có thể lưu trữ trạng thái kiểm tra như trong ví dụ sau:
class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { val myRepository = MyRepository.getInstance() provideContent { val scope = rememberCoroutineScope() val saveApple: (Boolean) -> Unit = { scope.launch { myRepository.saveApple(it) } } MyContent(saveApple) } } @Composable private fun MyContent(saveApple: (Boolean) -> Unit) { var isAppleChecked by remember { mutableStateOf(false) } Button( text = "Save", onClick = { saveApple(isAppleChecked) } ) } }
Bạn cũng có thể cung cấp thuộc tính colors
cho CheckBox
, Switch
và RadioButton
để tuỳ chỉnh màu sắc của các thành phần này:
CheckBox( // ... colors = CheckboxDefaults.colors( checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight), uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked } ) Switch( // ... colors = SwitchDefaults.colors( checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan), uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta), checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow), uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked }, text = "Enabled" ) RadioButton( // ... colors = RadioButtonDefaults.colors( checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow), uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue) ), )
Các thành phần bổ sung
Glance 1.1.0 bao gồm bản phát hành các thành phần bổ sung, như mô tả trong bảng sau:
Tên | Hình ảnh | Đường liên kết tham khảo | Ghi chú khác |
---|---|---|---|
Nút được tô màu nền | Thành phần | ||
Nút có đường viền | Thành phần | ||
Nút biểu tượng | Thành phần | Chính / Phụ / Chỉ biểu tượng | |
Thanh tiêu đề | Thành phần | ||
Scaffold | Khung và thanh tiêu đề nằm trong cùng một bản minh hoạ. |
Để biết thêm thông tin về thông tin chi tiết về thiết kế, hãy xem các thiết kế thành phần trong bộ công cụ thiết kế này trên Figma.
Để biết thêm thông tin về bố cục chuẩn, hãy truy cập vào bài viết Bố cục tiện ích chuẩn.