Các phần sau đây mô tả cách tạo một tiện ích ứng dụng cơ bản bằng Glance.
Khai báo AppWidget trong tệp kê khai
Sau khi hoàn tất các bước thiết lập, hãy khai báo AppWidget và siêu dữ liệu của lớp này trong ứng dụng.
Mở rộng trình nhận
AppWidgettừGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget") }
Đăng ký nhà cung cấp tiện ích ứng dụng trong tệp
AndroidManifest.xmlvà tệp siêu dữ liệu liên quan:<receiver android:name=".glance.MyReceiver" android:exported="true"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/my_app_widget_info" /> </receiver>
Thêm siêu dữ liệu AppWidgetProviderInfo
Tiếp theo, hãy làm theo hướng dẫn Tạo tiện ích để tạo và xác định thông tin tiện ích ứng dụng trong tệp @xml/my_app_widget_info.
Điểm khác biệt duy nhất đối với Glance là không có XML initialLayout, nhưng bạn phải xác định một XML. Bạn có thể sử dụng bố cục tải được xác định trước trong thư viện:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>
Khai báo XML AppWidgetProviderInfo
Đối tượng AppWidgetProviderInfo xác định các thuộc tính thiết yếu của tiện ích. Xác định AppWidgetProviderInfo trong tệp tài nguyên siêu dữ liệu XML
(res/xml/my_app_widget_info.xml) bên trong phần tử <appwidget-provider>:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:maxResizeWidth="250dp"
android:maxResizeHeight="120dp"
android:updatePeriodMillis="86400000"
android:description="@string/example_appwidget_description"
android:previewLayout="@layout/example_appwidget_preview"
android:initialLayout="@layout/glance_default_loading_layout"
android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>
Thuộc tính định cỡ tiện ích
Màn hình chính mặc định đặt các tiện ích trong cửa sổ của nó dựa trên một lưới các ô có chiều cao và chiều rộng xác định. Hầu hết màn hình chính chỉ cho phép tiện ích có kích thước là bội số nguyên của các ô lưới, ví dụ: hai ô theo chiều ngang và ba ô theo chiều dọc.
Các thuộc tính định cỡ tiện ích cho phép bạn chỉ định kích thước mặc định cho tiện ích và cung cấp giới hạn dưới và giới hạn trên về kích thước của tiện ích. Trong bối cảnh này, kích thước mặc định của tiện ích là kích thước mà tiện ích đó sử dụng khi được thêm vào màn hình chính lần đầu tiên.
Bảng sau đây mô tả các thuộc tính <appwidget-provider> liên quan đến kích thước tiện ích:
| Thuộc tính và nội dung mô tả | |
|---|---|
targetCellWidth và
targetCellHeight (Android 12),
minWidth và minHeight |
targetCellWidth và targetCellHeight, cũng như minWidth và minHeight) để ứng dụng có thể quay lại sử dụng minWidth và minHeight nếu thiết bị của người dùng không hỗ trợ targetCellWidth và targetCellHeight. Nếu được hỗ trợ, các thuộc tính targetCellWidth và targetCellHeight sẽ được ưu tiên hơn các thuộc tính minWidth và minHeight.
|
minResizeWidth và
minResizeHeight |
Chỉ định kích thước tối thiểu tuyệt đối của tiện ích. Các giá trị này chỉ định kích thước mà tiện ích không thể đọc được hoặc không thể sử dụng được. Khi sử dụng các thuộc tính này, người dùng có thể đổi kích thước tiện ích thành kích thước nhỏ hơn kích thước mặc định của tiện ích. Thuộc tính minResizeWidth sẽ bị bỏ qua nếu lớn hơn minWidth hoặc nếu bạn không bật tính năng đổi kích thước theo chiều ngang. Xem
resizeMode. Tương tự, thuộc tính minResizeHeight sẽ bị bỏ qua nếu lớn hơn minHeight hoặc nếu bạn không bật tính năng đổi kích thước theo chiều dọc. |
maxResizeWidth và
maxResizeHeight |
Chỉ định kích thước tối đa được đề xuất của tiện ích. Nếu các giá trị không phải là bội số của kích thước ô lưới, thì các giá trị đó sẽ được làm tròn lên kích thước ô gần nhất. Thuộc tính maxResizeWidth sẽ bị bỏ qua nếu nhỏ hơn minWidth hoặc nếu bạn không bật tính năng đổi kích thước theo chiều ngang. Xem resizeMode. Tương tự, thuộc tính maxResizeHeight sẽ bị bỏ qua nếu nhỏ hơn minHeight hoặc nếu bạn không bật tính năng đổi kích thước theo chiều dọc.
Ra mắt trong Android 12. |
resizeMode |
Chỉ định các quy tắc mà theo đó một tiện ích có thể được đổi kích thước. Bạn có thể sử dụng thuộc tính này để cho phép đổi kích thước tiện ích trên màn hình chính theo chiều ngang, chiều dọc hoặc cả hai trục. Người dùng chạm và giữ một tiện ích để hiện ô điều khiển thay đổi kích thước, sau đó kéo ô điều khiển ngang hoặc dọc để thay đổi kích thước của tiện ích trên lưới bố cục. Giá trị cho thuộc tính resizeMode bao gồm horizontal, vertical và none. Để khai báo một tiện ích có thể đổi kích thước theo chiều ngang và chiều dọc, hãy sử dụng horizontal|vertical. |
Ví dụ
Để minh hoạ cách các thuộc tính trong bảng trước ảnh hưởng đến kích thước tiện ích, hãy giả sử các thông số kỹ thuật sau:
- Một ô lưới có chiều rộng 30 dp và chiều cao 50 dp.
- Sau đây là quy cách của thuộc tính:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="80dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:minResizeWidth="40dp"
android:minResizeHeight="40dp"
android:maxResizeWidth="120dp"
android:maxResizeHeight="120dp"
android:resizeMode="horizontal|vertical" />
Kể từ Android 12:
Sử dụng các thuộc tính targetCellWidth và targetCellHeight làm kích thước mặc định của tiện ích.
Kích thước mặc định của tiện ích là 2x2. Bạn có thể đổi kích thước tiện ích xuống 2x1 hoặc lên 4x3.
Android 11 trở xuống:
Sử dụng các thuộc tính minWidth và minHeight để tính toán kích thước mặc định của tiện ích.
Chiều rộng mặc định = Math.ceil(80 / 30) = 3
Chiều cao mặc định = Math.ceil(80 / 50) = 2
Kích thước mặc định của tiện ích là 3x2. Bạn có thể đổi kích thước tiện ích xuống 2x1 hoặc lên toàn màn hình.
Các thuộc tính tiện ích bổ sung
Bảng sau đây mô tả các thuộc tính <appwidget-provider> liên quan đến các đặc điểm khác ngoài kích thước tiện ích.
| Thuộc tính và nội dung mô tả | |
|---|---|
updatePeriodMillis |
Xác định tần suất khung tiện ích yêu cầu cập nhật từ GlanceAppWidgetReceiver bằng cách gọi phương thức gọi lại onUpdate(). Bạn nên cập nhật càng ít càng tốt (không quá một lần mỗi giờ) để tiết kiệm pin.
Để biết thông tin chi tiết, hãy xem phần Thời điểm cập nhật tiện ích trong phần Quản lý trạng thái của Glance. |
initialLayout |
Trỏ đến tài nguyên bố cục xác định bố cục tải của tiện ích trước khi các thành phần giao diện người dùng Glance hiển thị. Bạn có thể sử dụng bố cục tải được xác định trước trong thư viện: @layout/glance_default_loading_layout. |
configure |
Xác định hoạt động cấu hình sẽ chạy khi người dùng thêm tiện ích. Hãy xem hướng dẫn Cho phép người dùng định cấu hình tiện ích ứng dụng. |
description |
Chỉ định nội dung mô tả cho bộ chọn tiện ích để hiển thị cho tiện ích của bạn. Ra mắt trong Android 12. |
previewLayout (Android 12)
và previewImage (Android 11 trở xuống) |
|
autoAdvanceViewId |
Chỉ định mã thành phần hiển thị của thành phần hiển thị phụ của tiện ích được máy chủ của tiện ích tự động chuyển. |
widgetCategory |
Khai báo xem tiện ích của bạn có thể hiển thị trên màn hình chính (home_screen), màn hình khoá (keyguard) hay cả hai hay không. Đối với Android 5.0 trở lên, chỉ home_screen là hợp lệ. |
widgetFeatures |
Khai báo các tính năng mà tiện ích hỗ trợ. Ví dụ: nếu cấu hình của tiện ích là không bắt buộc, hãy chỉ định cả configuration_optional và reconfigurable. |
Xác định GlanceAppWidget
Tạo một lớp mới mở rộng từ
GlanceAppWidgetvà ghi đè phương thứcprovideGlance. Đây là phương thức mà bạn có thể tải dữ liệu cần thiết để hiển thị tiện ích:class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // In this method, load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here Text("Hello World") } } }
Tạo thực thể cho lớp này trong
glanceAppWidgettrênGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { // Let MyAppWidgetReceiver know which GlanceAppWidget to use override val glanceAppWidget: GlanceAppWidget = MyAppWidget() }
Giờ đây, bạn đã định cấu hình AppWidget bằng Glance.
Quản lý bộ nhớ hình ảnh của tiện ích trên các nhóm kích thước
Khi tiện ích của bạn hiển thị bitmap hoặc URI hình ảnh content://, hãy giữ tổng bộ nhớ hình ảnh RemoteViews được chuyển đổi tuần tự trong giới hạn của hệ thống (6 * width *
height byte, được thực thi bằng IllegalArgumentException trên Android 17 trở lên) bằng cách làm như sau:
- Giải mã bitmap dùng chung trước
provideContent: Nếu tiện ích của bạn sử dụngSizeMode.ResponsivehoặcSizeMode.Exact, thì Glance sẽ chạy lambdaprovideContentmột lần cho mỗi bộ chứaDpSize. Việc giải mãBitmapbên trongprovideContentsẽ phân bổ một thực thểBitmapriêng biệt cho mỗi bộ chứa, nhân mức sử dụng bộ nhớ trongRemoteViews.mBitmapCache. Giải mã bitmap được lấy mẫu giảm (Bitmap.Config.RGB_565cho hình ảnh mờ) một lần bên trongprovideGlancetrướcprovideContentđể tất cả bộ chứa đều dùng chung một tệp đối chiếuBitmap. Sử dụng lệnh nhập
ImageProviderphù hợp: Jetpack Glance cung cấp hai phương thức nạp chồngImageProviderriêng biệt trên hai gói:- Sử dụng
import androidx.glance.ImageProviderkhi truyềnImageProvider(bitmap: Bitmap)trong bộ nhớ hoặc tài nguyên drawableImageProvider(resId: Int). - Sử dụng
import androidx.glance.appwidget.ImageProviderkhi truyền URIImageProvider(uri: Uri). Khi phân phát tệp đã lưu vào bộ nhớ đệm thông qua URIFileProvider, hãy cấp quyềnIntent.FLAG_GRANT_READ_URI_PERMISSIONcho mọi gói trình chạyIntent.CATEGORY_HOMEđã cài đặt để máy chủ có thể kết xuất hình ảnh.
- Sử dụng
Sử dụng lớp GlanceAppWidgetReceiver để xử lý các thông báo của tiện ích
Tiện ích GlanceAppWidgetReceiver điều phối các thông báo và thông tin cập nhật trạng thái nền tảng bằng cách mở rộng AppWidgetProvider cơ bản. Lớp này nhận các sự kiện của nền tảng khi tiện ích của bạn được cập nhật, xoá, bật hoặc tắt, sau đó chuyển các sự kiện này thành yêu cầu về vòng đời của Compose.
Khai báo tiện ích trong tệp kê khai
Khai báo lớp con của lớp GlanceAppWidgetReceiver dưới dạng một bộ nhận tín hiệu truyền tin trong tệp AndroidManifest.xml:
<receiver android:name="MyReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/my_app_widget_info" />
</receiver>
Phần tử <receiver> yêu cầu thuộc tính android:name, thuộc tính này chỉ định lớp nhận. Bộ thu phải chấp nhận hành động truyền tin ACTION_APPWIDGET_UPDATE bên trong <intent-filter>.
Phần tử <meta-data> phải xác định tên của phần tử là android.appwidget.provider và thuộc tính android:resource phải trỏ đến tài nguyên siêu dữ liệu XML AppWidgetProviderInfo (@xml/my_app_widget_info).
Triển khai lớp GlanceAppWidgetReceiver
Trong Glance, bạn mở rộng GlanceAppWidgetReceiver thay vì trực tiếp mở rộng AppWidgetProvider. Triển khai bằng cách liên kết thiết bị thu với thực thể GlanceAppWidget. Các lệnh gọi lại chính có trong GlanceAppWidgetReceiver hoạt động như sau:
onUpdate(): Tự động bị Glance ghi đè để thực thi các bản cập nhật thành phần. Nếu ghi đèonUpdatetheo cách thủ công, bạn phải gọisuper.onUpdateđể cho phép Glance khởi chạy thành công các luồng thành phần.onAppWidgetOptionsChanged(): Được gọi khi tiện ích được đặt lần đầu hoặc được đổi kích thước. Glance đọc các mục trong gói tuỳ chọn nâng cao để bố cục của bạn điều chỉnh liền mạch dựa trên kích thước thời gian chạy.onDeleted(Context, IntArray): Được gọi bất cứ khi nào người dùng xoá một thực thể tiện ích cụ thể.onEnabled(Context): Được kích hoạt khi thực thể đầu tiên của tiện ích được tạo thành công. Rất phù hợp để chạy các hoạt động di chuyển trên toàn cầu.onDisabled(Context): Được gọi khi phiên bản hoạt động gần đây nhất của nhà cung cấp bị xoá.onReceive(Context, Intent): Chặn mọi thông báo truyền tin của nền tảng trước các phương thức gọi lại cụ thể. Bạn phải đảm bảo rằng mọi logic của trình nhận tuỳ chỉnh mà bạn viết đều gọisuper.onReceive(context, intent)và không bao giờ được tự gọigoAsyncvì Glance tự động định tuyến công việc một cách không đồng bộ.
Nhận ý định truyền tin của tiện ích
Trong nền, GlanceAppWidgetReceiver lọc và xử lý các ý định truyền tin của tiện ích nền tảng cơ bản sau:
ACTION_APPWIDGET_UPDATEACTION_APPWIDGET_DELETEDACTION_APPWIDGET_ENABLEDACTION_APPWIDGET_DISABLEDACTION_APPWIDGET_OPTIONS_CHANGED
Tạo giao diện người dùng
Đoạn mã sau đây minh hoạ cách tạo giao diện người dùng:
/* Import Glance Composables In the event there is a name clash with the Compose classes of the same name, you may rename the imports per https://kotlinlang.org/docs/packages.html#imports using the `as` keyword. import androidx.glance.Button import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.text.Text */ class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // Load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here MyContent() } } @Composable private fun MyContent() { Column( modifier = GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.Top, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button( text = "Home", onClick = actionStartActivity<MyActivity>() ) Button( text = "Work", onClick = actionStartActivity<MyActivity>() ) } } } }
Mã mẫu trước đó thực hiện những việc sau:
- Trong
Columncấp cao nhất, các mục được đặt theo chiều dọc, mục này sau mục kia. Columnmở rộng kích thước để phù hợp với không gian có sẵn (thông quaGlanceModifiervà căn chỉnh nội dung lên trên cùng (verticalAlignment) và căn giữa theo chiều ngang (horizontalAlignment).- Nội dung của
Columnđược xác định bằng lambda. Thứ tự rất quan trọng.- Mục đầu tiên trong
Columnlà thành phầnTextcó khoảng đệm12.dp. - Mục thứ hai là
Row, trong đó các mục được đặt theo chiều ngang, mục này sau mục kia, với haiButtonsđược căn giữa theo chiều ngang (horizontalAlignment). Màn hình cuối cùng phụ thuộc vào không gian có sẵn. Hình ảnh sau đây là một ví dụ về giao diện của thông báo:
- Mục đầu tiên trong
Bạn có thể thay đổi giá trị căn chỉnh hoặc áp dụng các giá trị đối tượng sửa đổi khác (chẳng hạn như khoảng đệm) để thay đổi vị trí và kích thước của các thành phần. Hãy xem tài liệu tham khảo để biết danh sách đầy đủ các thành phần, tham số và đối tượng sửa đổi có sẵn cho mỗi lớp.
Triển khai góc bo tròn
Android 12 giới thiệu các tham số hệ thống để tuỳ chỉnh bán kính góc của tiện ích ứng dụng một cách linh động:
system_app_widget_background_radius: Chỉ định bán kính góc của vùng chứa nền tiện ích (không bao giờ lớn hơn 28 dp).- Bán kính bên trong: Để tránh bị cắt nội dung, hãy tính bán kính tỷ lệ cho nội dung bên trong dựa trên đường viền nền của hệ thống:
systemRadiusValue - widgetPadding
Trong Glance, bạn có thể áp dụng các thuộc tính định cỡ bán kính góc một cách linh động trong thành phần bằng cách sử dụng GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius).
Để có khả năng tương thích ngược trên các thiết bị chạy Android 11 (cấp độ API 30) trở xuống, hãy triển khai thuộc tính tùy chỉnh và phương án dự phòng tài nguyên giao diện tuỳ chỉnh:
/values/attrs.xml<resources> <attr name="backgroundRadius" format="dimension" /> </resources>/values/styles.xml<resources> <style name="MyWidgetTheme"> <item name="backgroundRadius">@dimen/my_background_radius_dimen</item> </style> </resources>/values-31/styles.xml<resources> <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight"> <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item> </style> </resources>/drawable/my_widget_background.xml<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="?attr/backgroundRadius" /> </shape>