Trang này mô tả các tinh chỉnh về kích thước tiện ích và tăng tính linh hoạt được giới thiệu trong Android 12 (API cấp 31). Bài viết này cũng trình bày chi tiết cách xác định kích thước cho tiện ích của bạn.
Dùng API cải tiến đối với kích thước và bố cục tiện ích
Kể từ Android 12 (API cấp 31), bạn có thể cung cấp thêm các thuộc tính kích thước được tinh chỉnh và bố cục linh hoạt bằng cách làm như sau, như mô tả trong các phần sau:
Chỉ định các quy tắc ràng buộc khác đối với kích thước tiện ích.
Cung cấp bố cục thích ứng hoặc bố cục chính xác.
Trong các phiên bản Android trước, bạn có thể lấy phạm vi kích thước của một tiện ích bằng cách sử dụng các dữ liệu bổ sung OPTION_APPWIDGET_MIN_WIDTH
, OPTION_APPWIDGET_MIN_HEIGHT
, OPTION_APPWIDGET_MAX_WIDTH
và OPTION_APPWIDGET_MAX_HEIGHT
rồi ước tính kích thước của tiện ích. Tuy nhiên, logic đó không hoạt động trong mọi trường hợp. Đối với các tiện ích nhắm mục tiêu đến Android 12 trở lên, bạn nên cung cấp bố cục thích ứng hoặc bố cục chính xác.
Chỉ định các giới hạn khác về kích thước tiện ích
Android 12 bổ sung các API giúp bạn đảm bảo tiện ích có kích thước ổn định hơn trên nhiều thiết bị có nhiều kích thước màn hình.
Ngoài các thuộc tính minWidth
, minHeight
, minResizeWidth
và minResizeHeight
hiện có, hãy sử dụng các thuộc tính appwidget-provider
mới sau:
targetCellWidth
vàtargetCellHeight
: xác định kích thước mục tiêu của tiện ích dựa trên các ô lưới của trình chạy. Nếu được xác định, các thuộc tính này sẽ được dùng thay chominWidth
hoặcminHeight
.maxResizeWidth
vàmaxResizeHeight
: xác định kích thước tối đa mà trình chạy cho phép người dùng đổi kích thước tiện ích.
XML sau đây cho biết cách sử dụng các thuộc tính kích thước.
<appwidget-provider
...
android:targetCellWidth="3"
android:targetCellHeight="2"
android:maxResizeWidth="250dp"
android:maxResizeHeight="110dp">
</appwidget-provider>
Cung cấp bố cục thích ứng
Nếu bố cục cần thay đổi tuỳ thuộc vào kích thước của tiện ích, bạn nên tạo một tập hợp bố cục nhỏ, mỗi bố cục hợp lệ với nhiều kích thước. Nếu không thể, bạn có thể cung cấp bố cục dựa trên kích thước tiện ích chính xác trong thời gian chạy, như mô tả trên trang này.
Tính năng này cho phép bạn mở rộng quy mô mượt mà hơn và tình trạng hệ thống tổng thể tốt hơn, vì hệ thống không phải đánh thức ứng dụng mỗi khi hiển thị tiện ích ở một kích thước khác.
Ví dụ về mã sau đây cho thấy cách cung cấp danh sách bố cục.
Kotlin
override fun onUpdate(...) { val smallView = ... val tallView = ... val wideView = ... val viewMapping: Map<SizeF, RemoteViews> = mapOf( SizeF(150f, 100f) to smallView, SizeF(150f, 200f) to tallView, SizeF(215f, 100f) to wideView ) val remoteViews = RemoteViews(viewMapping) appWidgetManager.updateAppWidget(id, remoteViews) }
Java
@Override public void onUpdate(...) { RemoteViews smallView = ...; RemoteViews tallView = ...; RemoteViews wideView = ...; Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); viewMapping.put(new SizeF(150f, 100f), smallView); viewMapping.put(new SizeF(150f, 200f), tallView); viewMapping.put(new SizeF(215f, 100f), wideView); RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews); }
Giả sử tiện ích có các thuộc tính sau:
<appwidget-provider
android:minResizeWidth="160dp"
android:minResizeHeight="110dp"
android:maxResizeWidth="250dp"
android:maxResizeHeight="200dp">
</appwidget-provider>
Đoạn mã trên có nghĩa như sau:
smallView
hỗ trợ từ 160 dp (minResizeWidth
) × 110 dp (minResizeHeight
) đến 160 dp × 199 dp (điểm cắt tiếp theo – 1 dp).tallView
hỗ trợ từ 160 dp × 200 dp đến 214 dp (điểm cắt tiếp theo – 1) × 200 dp.wideView
hỗ trợ từ 215 dp × 110 dp (minResizeHeight
) đến 250 dp (maxResizeWidth
) × 200 dp (maxResizeHeight
).
Tiện ích của bạn phải hỗ trợ khoảng kích thước từ minResizeWidth
× minResizeHeight
đến maxResizeWidth
× maxResizeHeight
. Trong phạm vi đó, bạn có thể quyết định điểm cắt để chuyển đổi bố cục.
![Ví dụ về bố cục thích ứng](https://developer.android.com/static/images/appwidgets/size-range.gif?authuser=8&hl=vi)
Cung cấp bố cục chính xác
Nếu không thể thực hiện một tập hợp nhỏ các bố cục thích ứng, bạn có thể cung cấp các bố cục khác được điều chỉnh cho phù hợp với kích thước mà tiện ích sẽ hiển thị. Đây thường là 2 kích thước cho điện thoại (chế độ dọc và ngang) và 4 kích thước cho thiết bị có thể gập lại.
Để triển khai giải pháp này, ứng dụng của bạn cần thực hiện các bước sau:
Quá tải
AppWidgetProvider.onAppWidgetOptionsChanged()
, được gọi khi tập hợp kích thước thay đổi.Gọi
AppWidgetManager.getAppWidgetOptions()
để trả vềBundle
chứa các kích thước.Truy cập vào khoá
AppWidgetManager.OPTION_APPWIDGET_SIZES
trênBundle
.
Ví dụ về mã sau đây cho thấy cách cung cấp bố cục chính xác.
Kotlin
override fun onAppWidgetOptionsChanged( context: Context, appWidgetManager: AppWidgetManager, id: Int, newOptions: Bundle? ) { super.onAppWidgetOptionsChanged(context, appWidgetManager, id, newOptions) // Get the new sizes. val sizes = newOptions?.getParcelableArrayList<SizeF>( AppWidgetManager.OPTION_APPWIDGET_SIZES ) // Check that the list of sizes is provided by the launcher. if (sizes.isNullOrEmpty()) { return } // Map the sizes to the RemoteViews that you want. val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews)) appWidgetManager.updateAppWidget(id, remoteViews) } // Create the RemoteViews for the given size. private fun createRemoteViews(size: SizeF): RemoteViews { }
Java
@Override public void onAppWidgetOptionsChanged( Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions); // Get the new sizes. ArrayList<SizeF> sizes = newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES); // Check that the list of sizes is provided by the launcher. if (sizes == null || sizes.isEmpty()) { return; } // Map the sizes to the RemoteViews that you want. Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); for (SizeF size : sizes) { viewMapping.put(size, createRemoteViews(size)); } RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews); } // Create the RemoteViews for the given size. private RemoteViews createRemoteViews(SizeF size) { }
Xác định kích thước cho tiện ích của bạn
Mỗi tiện ích phải xác định một targetCellWidth
và targetCellHeight
cho các thiết bị chạy Android 12 trở lên (hoặc minWidth
và minHeight
cho tất cả phiên bản Android) để cho biết mức dung lượng tối thiểu mà tiện ích đó sử dụng theo mặc định. Tuy nhiên, khi người dùng thêm một tiện ích vào màn hình chính, tiện ích đó thường sẽ chiếm nhiều hơn chiều rộng và chiều cao tối thiểu mà bạn chỉ định.
Màn hình chính của Android cung cấp cho người dùng một lưới không gian có sẵn để họ có thể đặt tiện ích và biểu tượng. Lưới này có thể khác nhau tuỳ theo thiết bị; ví dụ: nhiều điện thoại di động có lưới 5x4 và máy tính bảng có thể cung cấp lưới lớn hơn. Khi bạn thêm tiện ích, tiện ích đó sẽ được kéo giãn để chiếm số ô tối thiểu (theo chiều ngang và chiều dọc) cần thiết để đáp ứng các quy tắc ràng buộc đối với targetCellWidth
và targetCellHeight
trên thiết bị chạy Android 12 trở lên hoặc minWidth
và minHeight
trên thiết bị chạy Android 11 (API cấp 30) trở xuống.
Cả chiều rộng và chiều cao của một ô cũng như kích thước của lề tự động được áp dụng cho các tiện ích đều có thể khác nhau giữa các thiết bị. Sử dụng bảng sau đây để ước tính kích thước tối thiểu của tiện ích trong điện thoại di động dạng lưới 5x4 thông thường, dựa trên số lượng ô lưới bị chiếm dụng mà bạn muốn:
Số ô (chiều rộng x chiều cao) | Kích thước có sẵn ở chế độ dọc (dp) | Kích thước có sẵn ở chế độ ngang (dp) |
---|---|---|
1x1 | 57x102dp | 127x51dp |
2x1 | 130x102dp | 269x51dp |
3x1 | 203x102dp | 412x51dp |
4x1 | 276x102dp | 554x51dp |
5x1 | 349x102dp | 697x51dp |
5x2 | 349x220dp | 697x117dp |
5x3 | 349x337dp | 697x184dp |
5x4 | 349x455dp | 697x250dp |
... | ... | ... |
n x m | (73n – 16) x (118m – 16) | (142n – 15) x (66m – 15) |
Sử dụng kích thước ô ở chế độ dọc để cho biết giá trị mà bạn cung cấp cho các thuộc tính minWidth
, minResizeWidth
và maxResizeWidth
. Tương tự, hãy sử dụng kích thước ô ở chế độ ngang để cho biết các giá trị bạn cung cấp cho các thuộc tính minHeight
, minResizeHeight
và maxResizeHeight
.
Nguyên nhân là do chiều rộng của ô ở chế độ dọc thường nhỏ hơn so với ở chế độ ngang – và tương tự, chiều cao của ô ở chế độ ngang thường nhỏ hơn so với ở chế độ dọc.
Ví dụ: nếu muốn chiều rộng tiện ích có thể đổi kích thước xuống một ô trên Google Pixel 4, bạn cần đặt minResizeWidth
thành tối đa là 56 dp để đảm bảo giá trị của thuộc tính minResizeWidth
nhỏ hơn 57 dp – vì một ô có chiều dọc tối thiểu là 57 dp.
Tương tự như vậy, nếu muốn chiều cao của tiện ích có thể đổi kích thước trong một ô trên cùng một thiết bị, bạn cần đặt minResizeHeight
thành tối đa là 50 dp để đảm bảo giá trị của thuộc tính minResizeHeight
nhỏ hơn 51 dp – vì một ô có chiều cao tối thiểu là 51 dp ở chế độ ngang.
Mỗi tiện ích đều có thể đổi kích thước trong phạm vi kích thước từ thuộc tính minResizeWidth
/minResizeHeight
đến maxResizeWidth
/maxResizeHeight
, có nghĩa là tiện ích cần phải thích ứng với bất kỳ phạm vi kích thước nào giữa các tiện ích đó.
Ví dụ: để đặt kích thước mặc định của tiện ích tại vị trí, bạn có thể đặt các thuộc tính sau:
<appwidget-provider
android:targetCellWidth="3"
android:targetCellHeight="2"
android:minWidth="180dp"
android:minHeight="110dp">
</appwidget-provider>
Tức là kích thước mặc định của tiện ích là 3x2 ô, như được chỉ định bởi các thuộc tính targetCellWidth
và targetCellHeight
, hoặc 180×110dp, do minWidth
và minHeight
chỉ định cho các thiết bị chạy Android 11 trở xuống. Trong trường hợp sau, kích thước trong các ô có thể thay đổi tuỳ thuộc vào thiết bị.
Ngoài ra, để đặt phạm vi kích thước được hỗ trợ của tiện ích, bạn có thể đặt các thuộc tính sau:
<appwidget-provider
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:maxResizeWidth="530dp"
android:maxResizeHeight="450dp">
</appwidget-provider>
Như đã chỉ định trong các thuộc tính trước, chiều rộng của tiện ích có thể đổi kích thước từ 180dp thành 530dp và chiều cao từ 110dp thành 450dp. Sau đó, tiện ích này có thể đổi kích thước từ ô 3x2 thành 5x2, miễn là có các điều kiện sau:
- Thiết bị có lưới 5x4.
- Việc ánh xạ giữa số ô và kích thước có sẵn tính bằng dps sẽ tuân theo bảng cho thấy thông tin ước tính về kích thước tối thiểu trên trang này.
- Tiện ích này sẽ thích ứng với phạm vi kích thước đó.
Kotlin
val smallView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_small) val mediumView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_medium) val largeView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_large) val viewMapping: Map<SizeF, RemoteViews> = mapOf( SizeF(180f, 110f) to smallView, SizeF(270f, 110f) to mediumView, SizeF(270f, 280f) to largeView ) appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))
Java
RemoteViews smallView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_small); RemoteViews mediumView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_medium); RemoteViews largeView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_large); Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); viewMapping.put(new SizeF(180f, 110f), smallView); viewMapping.put(new SizeF(270f, 110f), mediumView); viewMapping.put(new SizeF(270f, 280f), largeView); RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews);
Giả sử tiện ích này sử dụng bố cục thích ứng được xác định trong các đoạn mã trước đó. Tức là bố cục chỉ định là R.layout.widget_weather_forecast_small
được sử dụng từ 180 dp (minResizeWidth
) x 110 dp (minResizeHeight
) đến 269 x 279 dp (điểm cắt tiếp theo – 1). Tương tự, R.layout.widget_weather_forecast_medium
được sử dụng từ 270x110dp đến 270x279dp và R.layout.widget_weather_forecast_large
được sử dụng từ270x280dp đến
530dp (maxResizeWidth
) x 450dp (maxResizeHeight
).
Khi người dùng đổi kích thước tiện ích, giao diện của tiện ích sẽ thay đổi để thích ứng với từng kích thước trong các ô, như trong các ví dụ sau.
![Ví dụ về tiện ích thời tiết ở kích thước lưới 3x2 nhỏ nhất. Giao diện người dùng cho thấy tên vị trí (Tokyo), nhiệt độ (14°) và biểu tượng cho biết thời tiết có mây rải rác một phần.](https://developer.android.com/static/images/appwidgets/weather-size-3x2.png?authuser=8&hl=vi)
R.layout.widget_weather_forecast_small
3x2.![Ví dụ về tiện ích thời tiết có kích thước "trung bình" 4x2. Việc đổi kích thước tiện ích
theo cách này được xây dựng trên toàn bộ giao diện người dùng từ kích thước tiện ích trước
và thêm nhãn "Nhiều mây hơn" cùng thông tin dự báo về nhiệt độ từ
4 giờ chiều đến 7 giờ tối.](https://developer.android.com/static/images/appwidgets/weather-size-4x2.png?authuser=8&hl=vi)
R.layout.widget_weather_forecast_medium
.![Ví dụ về tiện ích thời tiết có kích thước "trung bình" 5x2. Việc đổi kích thước tiện ích theo cách này sẽ dẫn đến giao diện người dùng giống như kích thước trước đó, ngoại trừ việc tiện ích được kéo dài bằng một độ dài ô để chiếm nhiều không gian theo chiều ngang hơn.](https://developer.android.com/static/images/appwidgets/weather-size-5x2.png?authuser=8&hl=vi)
R.layout.widget_weather_forecast_medium
.![Ví dụ về tiện ích thời tiết có kích thước "lớn" 5x3. Việc đổi kích thước tiện ích
theo cách này sẽ dựa trên tất cả giao diện người dùng với các kích thước tiện ích trước đây
và thêm một chế độ xem bên trong tiện ích có chứa thông tin dự báo thời tiết
vào thứ Ba và thứ Tư. Biểu tượng cho biết thời tiết nắng hoặc mưa
và nhiệt độ cao và thấp mỗi ngày.](https://developer.android.com/static/images/appwidgets/weather-size-5x3.png?authuser=8&hl=vi)
R.layout.widget_weather_forecast_large
.![Ví dụ về tiện ích thời tiết có kích thước "lớn" 5x4. Việc đổi kích thước tiện ích theo cách này được xây dựng dựa trên tất cả giao diện người dùng từ các kích thước tiện ích trước đó, đồng thời thêm thứ Năm và thứ Sáu (cùng với các biểu tượng tương ứng cho biết loại thời tiết cũng như nhiệt độ cao và thấp mỗi ngày).](https://developer.android.com/static/images/appwidgets/weather-size-5x4.png?authuser=8&hl=vi)
R.layout.widget_weather_forecast_large
.