Tạo một tiện ích đơn giản

Tiện ích ứng dụng là các chế độ xem ứng dụng dưới dạng thu nhỏ mà bạn có thể nhúng vào các ứng dụng khác (chẳng hạn như màn hình chính) và nhận các bản cập nhật định kỳ. Các thành phần hiển thị này được gọi là tiện ích trong giao diện người dùng. Bạn có thể phát hành một thành phần hiển thị bằng nhà cung cấp tiện ích ứng dụng (hoặc trình cung cấp tiện ích). Một thành phần ứng dụng chứa các tiện ích khác được gọi là máy chủ lưu trữ tiện ích ứng dụng (hay máy chủ lưu trữ tiện ích). Hình 1 cho thấy một tiện ích nhạc mẫu:

Ví dụ về tiện ích âm nhạc
Hình 1. Ví dụ về tiện ích âm nhạc.

Tài liệu này mô tả cách xuất bản tiện ích bằng nhà cung cấp tiện ích. Để biết thông tin chi tiết về cách tạo AppWidgetHost của riêng bạn cho các tiện ích ứng dụng lưu trữ, hãy xem bài viết Tạo máy chủ tiện ích.

Để biết thông tin về cách thiết kế tiện ích của bạn, hãy xem bài viết Tổng quan về tiện ích ứng dụng.

Thành phần tiện ích

Để tạo một tiện ích, bạn cần có các thành phần cơ bản sau đây:

Đối tượng AppWidgetProviderInfo
Mô tả siêu dữ liệu của một tiện ích, chẳng hạn như bố cục của tiện ích, tần suất cập nhật và lớp AppWidgetProvider. AppWidgetProviderInfo được định nghĩa trong XML, như mô tả trong tài liệu này.
Lớp AppWidgetProvider
Xác định các phương thức cơ bản cho phép bạn lập trình giao diện với tiện ích. Thông qua đó, bạn nhận được các thông báo truyền tin khi tiện ích được cập nhật, bật, tắt hoặc bị xoá. Bạn khai báo AppWidgetProvider trong tệp kê khai rồi triển khai như mô tả trong tài liệu này.
Xem bố cục
Xác định bố cục ban đầu cho tiện ích. Bố cục này được xác định trong XML, như mô tả trong tài liệu này.

Hình 2 cho thấy cách các thành phần này phù hợp với quy trình xử lý tổng thể của tiện ích ứng dụng.

Quy trình xử lý tiện ích ứng dụng
Hình 2. Quy trình xử lý tiện ích ứng dụng.

Nếu tiện ích của bạn cần cấu hình người dùng, hãy triển khai hoạt động cấu hình tiện ích ứng dụng. Hoạt động này cho phép người dùng sửa đổi các chế độ cài đặt tiện ích (ví dụ: múi giờ cho một tiện ích đồng hồ).

Bạn cũng nên cải tiến những điểm cải tiến sau đây: bố cục tiện ích linh hoạt, các tính năng nâng cao khác, tiện ích nâng cao, tiện ích bộ sưu tậptạo máy chủ lưu trữ tiện ích.

Khai báo tệp XML AppWidgetProviderInfo

Đối tượng AppWidgetProviderInfo xác định các khía cạnh thiết yếu của một tiện ích. Xác định đối tượng AppWidgetProviderInfo trong tệp tài nguyên XML bằng cách sử dụng một phần tử <appwidget-provider> duy nhất và lưu phần tử đó vào thư mục res/xml/ của dự án.

Lệnh này được minh hoạ trong ví dụ sau:

<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/example_loading_appwidget"
    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

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

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à 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 thông qua các ô lưới. Các thuộc tính này 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 theo lưới.
  • Thuộc tính minWidthminHeight chỉ định kích thước mặc định của tiện ích tính bằng dp. Nếu giá trị chiều rộng hoặc chiều cao tối thiểu của một tiện ích không khớp với kích thước của ô, thì các giá trị sẽ được làm tròn lên kích thước ô gần nhất.
Bạn nên chỉ định cả hai tập hợp thuộc tính – targetCellWidthtargetCellHeight, minWidthminHeight – để ứng dụng của bạn 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à theo đó 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 giá trị này 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. Hãy xem resizeMode. Tương tự, thuộc tính minResizeHeight sẽ bị bỏ qua nếu giá trị này 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.
maxResizeWidthmaxResizeHeight Chỉ định kích thước tối đa được đề xuất cho 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ị này sẽ được làm tròn thành 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 chưa bật tính năng đổi kích thước theo chiều ngang. Hãy xem resizeMode. Tương tự, thuộc tính maxResizeHeight sẽ bị bỏ qua nếu thuộc tính này 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. Ra mắt trong Android 12.
resizeMode Chỉ định các quy tắc mà bạn có thể đổi kích thước tiện ích. Bạn có thể sử dụng thuộc tính này để giúp các tiện ích trên màn hình chính có thể đổi kích thước 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 ô điều khiển thay đổi kích thước, sau đó kéo ô đ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. Giá trị của thuộc tính resizeMode bao gồm horizontal, verticalnone. Để khai báo một tiện ích có thể thay đổ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 việc định kích thước tiện ích, hãy giả sử các thông số kỹ thuật sau:

  • Ô lưới có kích thước rộng 30 dp và cao 50 dp.
  • Bạn có thể cung cấp thông số kỹ thuật sau đây về 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 thuộc tính targetCellWidthtargetCellHeight làm kích thước mặc định của tiện ích.

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

Android 11 trở xuống:

Sử dụng 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

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

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 chất lượng khác ngoài kích thước tiện ích.

Thuộc tính và mô tả
updatePeriodMillis Xác định tần suất khung tiện ích yêu cầu cập nhật từ AppWidgetProvider bằng cách gọi phương thức gọi lại onUpdate(). Quá trình cập nhật thực tế không đảm bảo sẽ diễn ra chính xác đúng thời điểm với giá trị này. Do đó, bạn nên cập nhật ít nhất có thể (không quá một lần mỗi giờ) để tiết kiệm pin. Để xem danh sách đầy đủ các yếu tố cần cân nhắc nhằm chọn khoảng thời gian cập nhật phù hợp, hãy xem bài viết Tối ưu hoá để cập nhật nội dung của tiện ích.
initialLayout Trỏ đến tài nguyên bố cục xác định bố cục tiện ích.
configure Xác định hoạt động chạy khi người dùng thêm tiện ích, cho phép họ định cấu hình các thuộc tính tiện ích. Hãy xem phần Cho phép người dùng định cấu hình tiện ích. Kể từ Android 12, ứng dụng của bạn có thể bỏ qua cấu hình ban đầu. Hãy xem phần Sử dụng cấu hình mặc định của tiện ích để biết thông tin chi tiết.
description Chỉ định nội dung mô tả cho bộ chọn tiện ích mà bạn muốn hiển thị. 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ể mở rộng. Bạn sẽ cung cấp bản xem trước này dưới dạng bố cục XML được đặt thành kích thước mặc định của tiện ích. Lý tưởng nhất là XML bố cục được chỉ định vì thuộc tính này giống với XML bố cục như tiện ích thực tế với các giá trị mặc định thực tế.
  • Trên Android 11 trở xuống, thuộc tính previewImage chỉ định bản xem trước giao diện của tiện ích sau khi được định cấu hình. Người dùng sẽ nhìn thấy bản xem trước này khi chọn tiện ích ứng dụng. Nếu không được cung cấp, người dùng sẽ thấy biểu tượng trình chạy của ứng dụng. Trường này tương ứng với thuộc tính android:previewImage trong phần tử <receiver> của tệp AndroidManifest.xml.
Lưu ý: Bạn nên chỉ định cả thuộc tính previewImagepreviewLayout để ứng dụng có thể quay lại sử dụng previewImage nếu thiết bị của người dùng không hỗ trợ previewLayout. Để biết thêm thông tin chi tiết, hãy xem phần Khả năng tương thích ngược với bản xem trước tiện ích có thể mở rộng.
autoAdvanceViewId Chỉ định mã chế độ xem của chế độ xem phụ của tiện ích do máy chủ lưu trữ của tiện ích tự động nâng cao.
widgetCategory Khai báo việc 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. Đố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 bạn muốn tiện ích sử dụng cấu hình mặc định khi người dùng thêm tiện ích đó, hãy chỉ định cả hai cờ configuration_optionalreconfigurable. Thao tác này sẽ bỏ qua việc khởi chạy hoạt động cấu hình sau khi người dùng thêm tiện ích. Sau đó, người dùng vẫn có thể định cấu hình lại tiện ích.

Sử dụng lớp AppWidgetProvider để xử lý thông báo truyền tin về tiện ích

Lớp AppWidgetProvider xử lý các thông báo truyền tin về tiện ích và cập nhật tiện ích để phản hồi các sự kiện trong vòng đời của tiện ích. Các phần sau mô tả cách khai báo AppWidgetProvider trong tệp kê khai rồi triển khai.

Khai báo tiện ích trong tệp kê khai

Trước tiên, hãy khai báo lớp AppWidgetProvider trong tệp AndroidManifest.xml của ứng dụng như trong ví dụ sau:

<receiver android:name="ExampleAppWidgetProvider"
                 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/example_appwidget_info" />
</receiver>

Phần tử <receiver> đòi hỏi thuộc tính android:name. Thuộc tính này chỉ định AppWidgetProvider mà tiện ích sử dụng. Bạn không được xuất thành phần này trừ phi một quy trình riêng cần truyền tin đến AppWidgetProvider của bạn, điều này thường không xảy ra.

Phần tử <intent-filter> phải bao gồm phần tử <action> có thuộc tính android:name. Thuộc tính này chỉ định rằng AppWidgetProvider chấp nhận thông báo truyền tin ACTION_APPWIDGET_UPDATE. Đây là thông báo duy nhất mà bạn phải khai báo rõ ràng. AppWidgetManager sẽ tự động gửi tất cả các tin truyền tiện ích khác đến AppWidgetProvider nếu cần.

Phần tử <meta-data> chỉ định tài nguyên AppWidgetProviderInfo và cần có các thuộc tính sau:

  • android:name: chỉ định tên siêu dữ liệu. Sử dụng android.appwidget.provider để xác định dữ liệu dưới dạng chỉ số mô tả AppWidgetProviderInfo.
  • android:resource: chỉ định vị trí tài nguyên AppWidgetProviderInfo.

Triển khai lớp AppWidgetProvider

Lớp AppWidgetProvider mở rộng BroadcastReceiver dưới dạng một lớp tiện lợi để xử lý các thông báo truyền tin về tiện ích. Tệp này chỉ nhận được những thông báo sự kiện có liên quan đến tiện ích đó, chẳng hạn như khi tiện ích được cập nhật, xoá, bật và tắt. Khi những sự kiện truyền tin này xảy ra, các phương thức AppWidgetProvider sau đây sẽ được gọi:

onUpdate()
Hàm này được gọi để cập nhật tiện ích tại các khoảng thời gian được xác định bằng thuộc tính updatePeriodMillis trong AppWidgetProviderInfo. Xem bảng mô tả các thuộc tính tiện ích bổ sung trong trang này để biết thêm thông tin.
Phương thức này cũng được gọi khi người dùng thêm tiện ích. Vì vậy, phương thức này sẽ thực hiện các bước thiết lập cần thiết, chẳng hạn như xác định trình xử lý sự kiện cho các đối tượng View hoặc bắt đầu các công việc để tải dữ liệu để hiển thị trong tiện ích. Tuy nhiên, nếu bạn khai báo một hoạt động cấu hình không có cờ configuration_optional, thì phương thức này sẽ không được gọi khi người dùng thêm tiện ích, nhưng sẽ được gọi cho các lần cập nhật tiếp theo. Hoạt động cấu hình có trách nhiệm thực hiện lần cập nhật đầu tiên khi cấu hình hoàn tất. Xem Cho phép người dùng định cấu hình tiện ích ứng dụng để biết thêm thông tin.
Lệnh gọi lại quan trọng nhất là onUpdate(). Hãy xem bài viết Xử lý các sự kiện bằng lớp onUpdate() trong trang này để biết thêm thông tin.
onAppWidgetOptionsChanged()

Lệnh này được gọi khi tiện ích được đặt lần đầu tiên và bất cứ khi nào tiện ích được đổi kích thước. Hãy sử dụng lệnh gọi lại này để hiện hoặc ẩn nội dung dựa trên phạm vi kích thước của tiện ích. Nhận phạm vi kích thước và kể từ Android 12, danh sách kích thước có thể có mà một thực thể tiện ích có thể đáp ứng bằng cách gọi getAppWidgetOptions(). Thao tác này sẽ trả về Bundle bao gồm các nội dung sau:

  • OPTION_APPWIDGET_MIN_WIDTH: chứa giới hạn dưới đối với chiều rộng (tính bằng đơn vị dp) của một thực thể tiện ích.
  • OPTION_APPWIDGET_MIN_HEIGHT: chứa giới hạn dưới về chiều cao (tính bằng đơn vị dp) của một thực thể tiện ích.
  • OPTION_APPWIDGET_MAX_WIDTH: chứa giới hạn trên đối với chiều rộng (tính bằng đơn vị dp) của một thực thể tiện ích.
  • OPTION_APPWIDGET_MAX_HEIGHT: chứa giới hạn trên thuộc chiều cao (tính bằng đơn vị dp) của một thực thể tiện ích.
  • OPTION_APPWIDGET_SIZES: chứa danh sách các kích thước có thể (List<SizeF>), tính bằng đơn vị dp, mà một thực thể tiện ích có thể sử dụng. Ra mắt trong Android 12.
onDeleted(Context, int[])

Thao tác này được gọi mỗi khi một tiện ích bị xoá khỏi máy chủ tiện ích.

onEnabled(Context)

Lệnh này được gọi khi một bản sao của tiện ích được tạo lần đầu tiên. Ví dụ: nếu người dùng thêm hai phiên bản của tiện ích, thì thao tác này chỉ được gọi lần đầu tiên. Nếu bạn cần mở một cơ sở dữ liệu mới hoặc thực hiện một quy trình thiết lập khác mà chỉ cần diễn ra một lần cho tất cả các thực thể tiện ích, thì đây là việc nên làm.

onDisabled(Context)

Lệnh này được gọi khi thực thể gần đây nhất của tiện ích bị xoá khỏi máy chủ lưu trữ tiện ích. Đây là nơi bạn dọn dẹp mọi công việc đã thực hiện trong onEnabled(Context), chẳng hạn như xoá cơ sở dữ liệu tạm thời.

onReceive(Context, Intent)

Lệnh này được gọi cho mỗi thông báo truyền tin và trước mỗi phương thức gọi lại trước đó. Thông thường, bạn không cần triển khai phương thức này vì phương thức triển khai AppWidgetProvider mặc định sẽ lọc tất cả các tin nhắn truyền phát tiện ích và gọi các phương thức trước đó nếu phù hợp.

Bạn phải khai báo việc triển khai lớp AppWidgetProvider dưới dạng broadcast receiver bằng cách sử dụng phần tử <receiver> trong AndroidManifest. Vui lòng xem nội dung Khai báo tiện ích trong tệp kê khai trên trang này để biết thêm thông tin.

Xử lý các sự kiện bằng lớp onUpdate()

Lệnh gọi lại AppWidgetProvider quan trọng nhất là onUpdate(), vì lệnh này được gọi khi mỗi tiện ích được thêm vào máy chủ lưu trữ, trừ phi bạn sử dụng một hoạt động cấu hình không có cờ configuration_optional. Nếu tiện ích của bạn chấp nhận mọi sự kiện tương tác của người dùng, hãy đăng ký trình xử lý sự kiện trong lệnh gọi lại này. Nếu tiện ích của bạn không tạo tệp hoặc cơ sở dữ liệu tạm thời, hay thực hiện tác vụ khác cần dọn dẹp, thì onUpdate() có thể là phương thức gọi lại duy nhất mà bạn cần xác định.

Ví dụ: nếu muốn một tiện ích có nút khởi chạy một hoạt động khi được nhấn, bạn có thể sử dụng cách triển khai AppWidgetProvider sau:

Kotlin

class ExampleAppWidgetProvider : AppWidgetProvider() {

    override fun onUpdate(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetIds: IntArray
    ) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        appWidgetIds.forEach { appWidgetId ->
            // Create an Intent to launch ExampleActivity.
            val pendingIntent: PendingIntent = PendingIntent.getActivity(
                    /* context = */ context,
                    /* requestCode = */  0,
                    /* intent = */ Intent(context, ExampleActivity::class.java),
                    /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            val views: RemoteViews = RemoteViews(
                    context.packageName,
                    R.layout.appwidget_provider_layout
            ).apply {
                setOnClickPendingIntent(R.id.button, pendingIntent)
            }

            // Tell the AppWidgetManager to perform an update on the current
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

Java

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        for (int i=0; i < appWidgetIds.length; i++) {
            int appWidgetId = appWidgetIds[i];
            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(
                /* context = */ context,
                /* requestCode = */ 0,
                /* intent = */ intent,
                /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
            );

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

AppWidgetProvider này chỉ xác định phương thức onUpdate(), sử dụng phương thức này để tạo PendingIntent chạy Activity và đính kèm phương thức đó vào nút của tiện ích bằng cách sử dụng setOnClickPendingIntent(int, PendingIntent). Lớp này bao gồm một vòng lặp lặp lại qua từng mục nhập trong appWidgetIds. Đây là một mảng các mã nhận dạng xác định từng tiện ích do trình cung cấp này tạo. Nếu người dùng tạo nhiều thực thể của tiện ích, thì tất cả các thực thể đó sẽ cập nhật đồng thời. Tuy nhiên, chỉ có một lịch biểu updatePeriodMillis được quản lý cho tất cả các bản sao của tiện ích này. Ví dụ: nếu lịch cập nhật được xác định là hai giờ một lần và thực thể thứ hai của tiện ích được thêm một giờ sau lần đầu tiên, thì cả hai đều được cập nhật vào khoảng thời gian do lần cập nhật đầu tiên xác định và khoảng thời gian cập nhật thứ hai sẽ bị bỏ qua. Cả hai đều cập nhật hai giờ một lần, không phải mỗi giờ.

Hãy xem lớp mẫu ExampleAppWidgetProvider.java để biết thêm thông tin chi tiết.

Nhận ý định truyền tin bằng tiện ích

AppWidgetProvider là một lớp tiện lợi. Nếu muốn trực tiếp nhận thông báo về tiện ích, bạn có thể triển khai BroadcastReceiver của riêng mình hoặc ghi đè lệnh gọi lại onReceive(Context,Intent). Các ý định bạn cần quan tâm là:

Tạo bố cục tiện ích

Bạn phải xác định bố cục ban đầu cho tiện ích trong XML và lưu bố cục đó vào thư mục res/layout/ của dự án. Tham khảo Nguyên tắc thiết kế để biết thông tin chi tiết.

Việc tạo bố cục tiện ích rất đơn giản nếu bạn đã quen thuộc với bố cục. Tuy nhiên, hãy lưu ý rằng bố cục tiện ích dựa trên RemoteViews, không hỗ trợ mọi loại bố cục hoặc tiện ích khung hiển thị. Bạn không thể sử dụng thành phần hiển thị tuỳ chỉnh hoặc lớp con của thành phần hiển thị mà RemoteViews hỗ trợ.

RemoteViews cũng hỗ trợ ViewStub, một View vô hình, có kích thước bằng 0 mà bạn có thể sử dụng để tăng cường từng phần tài nguyên bố cục trong thời gian chạy.

Hỗ trợ hành vi có trạng thái

Android 12 hỗ trợ thêm cho hành vi trạng thái bằng cách sử dụng các thành phần hiện có sau đây:

Tiện ích này vẫn không có trạng thái. Ứng dụng phải lưu trữ trạng thái và đăng ký các sự kiện thay đổi trạng thái.

Ví dụ về tiện ích danh sách mua sắm thể hiện hành vi có trạng thái
Hình 3. Ví dụ về hành vi của trạng thái.

Ví dụ về mã sau đây cho thấy cách triển khai các thành phần này.

Kotlin

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
        R.id.my_checkbox,
        RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

Java

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true);

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2);

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
    R.id.my_checkbox,
    RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));

Cung cấp 2 bố cục: một bố cục nhắm đến các thiết bị chạy Android 12 trở lên trong res/layout-v31 và bố cục còn lại nhắm đến Android 11 cũ trở xuống trong thư mục res/layout mặc định.

Triển khai bo tròn góc

Android 12 giới thiệu các tham số hệ thống sau đây để đặt giá trị cho góc bo tròn của tiện ích:

  • system_app_widget_background_radius: bán kính góc của nền tiện ích, không bao giờ lớn hơn 28 dp.

  • system_app_widget_inner_radius: bán kính góc của bất kỳ khung hiển thị nào bên trong tiện ích. Kích thước này chính xác nhỏ hơn 8 dp so với bán kính nền để căn chỉnh phù hợp khi sử dụng khoảng đệm 8 dp.

Ví dụ sau đây cho thấy một tiện ích sử dụng system_app_widget_background_radius cho góc của tiện ích và system_app_widget_inner_radius cho các khung hiển thị bên trong tiện ích.

Tiện ích hiển thị bán kính của nền tiện ích và chế độ xem bên trong tiện ích
Hình 4. Góc bo tròn.

1 Góc của tiện ích.

2 Góc của một chế độ xem bên trong tiện ích.

Những điểm quan trọng cần lưu ý về góc bo tròn

  • Trình chạy và nhà sản xuất thiết bị bên thứ ba có thể ghi đè tham số system_app_widget_background_radius sao cho có kích thước nhỏ hơn 28 dp. Tham số system_app_widget_inner_radius luôn nhỏ hơn 8 dp so với giá trị của system_app_widget_background_radius.
  • Nếu tiện ích của bạn không sử dụng @android:id/background hoặc xác định nền sẽ cắt nội dung dựa trên đường viền (android:clipToOutline được đặt thành true), thì trình chạy sẽ tự động xác định nền và cắt tiện ích đó bằng cách sử dụng hình chữ nhật có các góc bo tròn tối đa 16 dp. Hãy xem bài viết Đảm bảo tiện ích của bạn tương thích với Android 12.

Để tương thích tiện ích với các phiên bản Android trước, bạn nên xác định các thuộc tính tuỳ chỉnh và sử dụng một giao diện tuỳ chỉnh để ghi đè các thuộc tính đó cho Android 12, như minh hoạ trong các tệp XML mẫu sau:

/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>

/layout/my_widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:background="@drawable/my_widget_background" />