Tạo tiện ích ứng dụng bằng tính năng Xem nhanh

Các phần sau đây mô tả cách tạo tiện ích cơ bản cho ứng dụng 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 tiện ích đó trong ứng dụng của bạn.

  1. Mở rộng trình nhận AppWidget từ GlanceAppWidgetReceiver:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
        override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
    }

  2. Đăng ký nhà cung cấp tiện ích cho ứng dụng trong tệp AndroidManifest.xml và tệp siêu dữ liệu được liên kết:

        <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 về tiện ích cho ứ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 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 đặc điểm 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 kích thước tiện ích

Theo mặc định, màn hình chính sẽ đặt các tiện ích vào cửa sổ dựa trên một lưới ô 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 các tiện ích có kích thước là bội số nguyên của các ô trong lưới, ví dụ: 2 ô theo chiều ngang và 3 ô theo chiều dọc.

Các thuộc tính kích thướ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 các giới hạn dưới và 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 một tiện ích là kích thước mà tiện ích đó có 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ả
targetCellWidthtargetCellHeight (Android 12), minWidthminHeight
  • Kể từ Android 12, các thuộc tính targetCellWidthtargetCellHeight sẽ chỉ định kích thước mặc định của tiện ích theo các ô trong lưới. Các thuộc tính này sẽ bị bỏ qua trong Android 11 trở xuống và có thể bị bỏ qua nếu màn hình chính không hỗ trợ bố cục dựa trên lưới.
  • Các thuộc tính minWidthminHeight chỉ định kích thước mặc định của tiện ích theo dp. Nếu các giá trị cho chiều rộng hoặc chiều cao tối thiểu của một tiện ích không khớp kích thước của các ô, thì các giá trị đó sẽ được làm tròn lên đến kích thước ô gần nhất.
Bạn nên chỉ định cả hai nhóm thuộc tính—targetCellWidthtargetCellHeight, cũng như minWidthminHeight—để ứng dụng có thể quay lại sử dụng minWidthminHeight nếu thiết bị của người dùng không hỗ trợ targetCellWidthtargetCellHeight. Nếu được hỗ trợ, các thuộc tính targetCellWidthtargetCellHeight sẽ được ưu tiên hơn các thuộc tính minWidthminHeight.
minResizeWidthminResizeHeight 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 đọc được hoặc không sử dụng được. Việc sử dụng các thuộc tính này cho phép người dùng đổ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 tính năng đổi kích thước theo chiều ngang không được bật. 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 tính năng đổi kích thước theo chiều dọc không được bật.
maxResizeWidthmaxResizeHeight 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 ô trong lưới, thì các giá trị đó sẽ được làm tròn lên đế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 tính năng đổi kích thước theo chiều ngang không được bật. Xem resizeMode. Tương tự, thuộc tính maxResizeHeight sẽ bị bỏ qua nếu nhỏ hơn minHeight hoặc nếu tính năng đổi kích thước theo chiều dọc không được bật. Ra mắt trong Android 12.
resizeMode Chỉ định các quy tắc mà theo đó có thể đổi kích thước tiện ích. 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 trên cả hai trục. Người dùng chạm và giữ một tiện ích để hiển thị các điểm điều khiển đổi kích thước, sau đó kéo các điểm điều khiển theo chiều ngang hoặc chiều dọc để thay đổi kích thước của tiện ích đó trên lưới bố cục. Các giá trị cho thuộc tính resizeMode bao gồm horizontal, verticalnone. Để 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 ô trong lưới có chiều rộng 30 dp và chiều cao 50 dp.
  • Thông số kỹ thuật sau đây về thuộc tính được cung cấp:
<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" />

Bắt đầu từ Android 12:

Sử dụng các thuộc tính targetCellWidthtargetCellHeight làm kích thước mặc định của tiện ích.

Theo mặc định, kích thước của tiện ích là 2x2. Bạn có thể đổi kích thước tiện ích xuống còn 2x1 hoặc lên đến 4x3.

Android 11 trở xuống:

Sử dụng các thuộc tính minWidthminHeight để 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

Theo mặc định, kích thước của tiện ích là 3x2. Bạn có thể đổi kích thước tiện ích xuống còn 2x1 hoặc lên đến toàn màn hình.

Thuộc tính bổ sung của tiện ích

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 bài viết Quản lý trạng thái 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 kết xuất. Bạn có thể sử dụng bố cục tải được xác định trước có trong thư viện: @layout/glance_default_loading_layout.
configure Xác định hoạt động định cấu hình sẽ chạy khi người dùng thêm tiện ích. Hãy xem phần Triển khai Hoạt động định cấu hình tiện ích trên trang này.
description Chỉ định nội dung mô tả để 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)
  • Kể từ Android 12, thuộc tính previewLayout sẽ chỉ định bản xem trước có thể điều chỉnh tỷ lệ mà bạn cung cấp dưới dạng một tập hợp bố cục XML được đặt thành kích thước mặc định của tiện ích. Tốt nhất là thuộc tính này trỏ đến một bản đồ XML tĩnh khớp với bố cục thiết kế của bạn.
  • Trong Android 11 trở xuống, thuộc tính previewImage sẽ chỉ định ảnh chụp màn hình tĩnh của hình ảnh có thể vẽ về giao diện của tiện ích, xuất hiện trong bộ chọn tiện ích.
Bạn nên chỉ định cả hai thuộc tính để ứng dụng của bạn quay lại các nền tảng cũ một cách suôn sẻ. Đối với các nền tảng mới hơn (Android 15 trở lên), bạn có thể xác định bản xem trước được tạo trực tiếp trong Kotlin bằng cách sử dụng `GlanceAppWidget.providePreview`. Hãy xem hướng dẫn Bản xem trước được tạo.
autoAdvanceViewId Chỉ định mã khung hiển thị của khung hiển thị phụ của tiện ích được máy chủ của tiện ích tự động chuyển sang.
widgetCategory Khai báo xem tiện ích của bạn có thể xuất hiện trên màn hình chính (home_screen), màn hình khoá (keyguard) hay cả hai. Đối với Android 5.0 trở lên, chỉ có home_screen là hợp lệ.
widgetFeatures Khai báo các tính năng được 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_optionalreconfigurable.

Xác định GlanceAppWidget

  1. Tạo một lớp mới mở rộng từ GlanceAppWidget và ghi đè phương thức provideGlance. Đây là phương thức mà bạn có thể tải dữ liệu cần thiết để kết xuất 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")
            }
        }
    }

  2. Tạo thực thể trong glanceAppWidget trên GlanceAppWidgetReceiver:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    
        // Let MyAppWidgetReceiver know which GlanceAppWidget to use
        override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
    }

Bây giờ, bạn đã định cấu hình AppWidget bằng Glance.

Sử dụng lớp GlanceAppWidgetReceiver để xử lý các lượt truyền tin của tiện ích

GlanceAppWidgetReceiver điều phối các lượt truyền tin của tiện ích và các bản 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 nền tảng khi tiện ích của bạn được cập nhật, xoá, bật hoặc tắt, chuyển các sự kiện đó thành các 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 GlanceAppWidgetReceiver làm một bộ nhận tín hiệu truyền tin trong tệp AndroidManifest.xml:

<receiver android:name="ExampleAppWidgetReceiver"
          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 receiver. Receiver 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 của bạn (@xml/my_app_widget_info).

Triển khai lớp GlanceAppWidgetReceiver

Trong Glance, bạn sẽ mở rộng GlanceAppWidgetReceiver thay vì AppWidgetProvider trực tiếp. Triển khai bằng cách liên kết receiver 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 bạn ghi đè onUpdate theo cách thủ công, bạn phải gọi super.onUpdate để cho phép Glance chạy các luồng thành phần thành công.
  • onAppWidgetOptionsChanged(): Được gọi khi tiện ích được đặt hoặc đổi kích thước lần đầu tiên. Glance đọc các mục trong gói tuỳ chọn ở chế độ nền để 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. Tuyệt vời để chạy các hoạt động di chuyển toàn cầu.
  • onDisabled(Context): Được gọi khi thực thể hoạt động cuối cùng của nhà cung cấp bị xoá.
  • onReceive(Context, Intent): Chặn mọi lượt 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 receiver tuỳ chỉnh mà bạn viết đều gọi super.onReceive(context, intent)không bao giờ được gọi goAsync vì 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

Ở chế độ nền, GlanceAppWidgetReceiver sẽ lọc và xử lý các ý định truyền tin cơ bản sau đây của tiện ích nền tảng:

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:

  • Ở cấp cao nhất Column, các mục được đặt theo chiều dọc lần lượt.
  • Column mở rộng kích thước để khớp với không gian có sẵn (thông qua GlanceModifier và căn chỉnh nội dung của cột đó lên trên (verticalAlignment) và căn giữa theo chiều ngang (horizontalAlignment).
  • Nội dung của Column được xác định bằng cách sử dụng lambda. Thứ tự rất quan trọng.
    • Mục đầu tiên trong Column là thành phần Text có phần đệm 12.dp.
    • Mục thứ hai là Row, trong đó các mục được đặt theo chiều ngang lần lượt, với 2 Buttons đượ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à ví dụ về giao diện có thể có:
destination_widget
Hình 1. Ví dụ về giao diện người dùng.

Bạn có thể thay đổi các giá trị căn chỉnh hoặc áp dụng các giá trị đối tượng sửa đổi khác nhau (chẳng hạn như phần đệ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 từng 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 cho ứng dụng một cách linh hoạt:

  • 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: Để ngăn nội dung bị cắt, hãy tính toán bán kính tỷ lệ cho nội dung bên trong dựa trên đường viền nền hệ thống: systemRadiusValue - widgetPadding

Trong Glance, bạn có thể áp dụng các thuộc tính kích thước bán kính góc một cách linh hoạt 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 các thuộc tính tuỳ chỉnh và các phương án dự phòng về tài nguyên chủ đề 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>